Java锁的正确使用

以对用户ID加锁为例

错误案例一

public void sync1(String uid){
	synchronized(uid){
		// 业务处理
	}
}

案例分析:这里原本想实现对相同用户的业务处理进行同步,但在多线程环境下,uid 并不是同一个对象,所以这里并不能实现代码同步。

改进案例一

public void sync2(String uid){
	synchronized(uid.intern()){
		// 业务处理
	}
}

案例分析:这里通过使用字符串的 intern 方法实现对同一个对象加锁,虽然可以实现代码同步,但这种方案存在一些问题:

  1. 后续如果用户ID和商品ID都需要同步处理,那么相同用户ID和商品ID无法并发处理(可以通过加个前缀标识处理);
  2. 常量池中的数据不好清楚(考虑使用 Map 代替)

改进案例二

private static final ConcurrentMap<String, Object> UID_MAP = new ConcurrentHashMap<>(); 
public void sync3(String uid){
	Object mutex = UID_MAP.computeIfAbsent(uid, k -> new Object());
	synchronized(mutex){
		// 业务处理
	}
}

案例分析:通过使用 ConcurrentMap 来代替字符串常量池。
注意:清除时需保证没有线程阻塞在对应 pid 的mutex 对象上,否则保证不了线程安全。

错误案例二

private static final ConcurrentMap<String, Object> UID_MAP = new ConcurrentHashMap<>(); 
public void sync4(String uid){
	Object mutex = UID_MAP.computeIfAbsent(uid, k -> new Object());
	synchronized(mutex){
		// 业务处理
	}
	UID_MAP.remove(uid);
}

案例分析:清除时需没有线程阻塞在对应 pid 的mutex 对象上,这里没有额外处理所以保证不了线程安全。
例如:线程 A、B 同时进入 sync4,线程 A 获取到锁后进入同步代码块进行业务处理,线程 B 阻塞。当线程 A 执行完业务处理后移除 uid 对应的 mutex 对象,线程 C 进入 sync4,线程 C 成功发现不存在 uid 则创建新的 mutex 对象,此时线程 B、C 就会同时执行业务处理。

改进案例三

private static final ConcurrentMap<String, Object> UID_MAP = new ConcurrentHashMap<>(); 
public void sync4(String uid){
	for( ; ;){
		Object mutex = UID_MAP.computeIfAbsent(uid, k -> new Object());
		synchronized(mutex){
			if(mutex != UID_MAP.get(uid)){
				continue;
			}
			// 业务处理
		}
		UID_MAP.remove(uid);
		break;
	}
}

案例分析:使用双重校验的思想来解决错误案例二的问题。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Java多线程中,使用是为了控制对共享资源的访问,以避免多个线程同时对同一资源进行修改而导致数据不一致或竞态条件的问题。Java提供了两种的机制:synchronized关键字和Lock接口。 1. synchronized关键字: - synchronized关键字可以用来修饰方法或代码块,使其成为同步方法或同步块。 - 当一个线程访问同步方法或同步块时,会自动获取该方法或代码块所在对象的,并在执行完后释放。 - 其他线程在获取之前会被阻塞,直到被释放。 - 示例代码: ```java public synchronized void synchronizedMethod() { // 同步方法 } public void synchronizedBlock() { synchronized (this) { // 同步块 } } ``` 2. Lock接口: - Lock接口是Java提供的显示机制,提供了更灵活的定方式。 - Lock接口的常用实现类是ReentrantLock,它具有与synchronized相似的语义。 - 示例代码: ```java Lock lock = new ReentrantLock(); public void lockMethod() { lock.lock(); try { // 加锁的代码 } finally { lock.unlock(); // 必须在finally块中释放,以防止异常导致无法释放 } } ``` 在使用时,需要注意以下几点: - 的粒度应尽量小,只定必要的代码块,以减少线程间的竞争。 - 避免死,即多个线程相互等待对方释放的情况。 - 保证正确使用,避免忘记释放或错误地释放,可以使用try-finally语句块来确保的释放。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值