使用CAS代替synchronized

在开发当中需要经常用到synchronized保证代码线程安全,在竞争条件下会阻塞等待资源,如果允许竞争不到资源返回失败,就可以使用cas减少阻塞时间。先来看一个cas的单例模式。

public class NonBlock {

	private static volatile NonBlock nonBlock;

	private static AtomicBoolean atomicBoolean = new AtomicBoolean(false);

	public static NonBlock getInstance() {
		if (nonBlock == null) {
			if (atomicBoolean.compareAndSet(false, true)) {
				nonBlock = new NonBlock();
			}
		}
		return nonBlock;
	}
}
在这个单例模式中,不同于synchronized的阻塞,多线程环境下,getInstance确保只会创建一个对象的情况下,可能返回的nonBlock是一个空对象。但,如果允许返回空对象的情况下,使用cas性能会比synchronized阻塞要好。

来看另一个例子,抢红包,每次抢红包之前,我们需要锁了RedPacket对象,以此来保证balance和num不会出现负数的情况。

	public class RedPacket {

		private long balance;

		private int num;

		public RedPacket(long balance, int num) {
			this.balance = balance;
			this.num = num;
		}

		public long get() {
			if (balance < 1 || num < 1) {
				return -1;
			}
			if (num == 1) {
				long result = balance;
				balance = 0;
				num--;
				return result;
			}
			long average = balance / num;
			long result = ThreadLocalRandom.current().nextLong(1, average * 2);
			balance -= result;
			num--;
			return result;
		}
我们还可以使用cas达到非阻塞的目的,这样能保证线程安全,出现竞争情况就提示抢失败,确点就是提示抢失败还可能余额大于0,先来不一定能抢到,后来人还能抢。

public class RedPacket {

		private long balance;

		private AtomicInteger num;

		public RedPacket(long balance, int num) {
			this.balance = balance;
			this.num = new AtomicInteger(num);
		}

		public long get() {
			int number = num.get();
			long balan = balance;
			if (balan < 1 || number < 1) {
				return -1;
			}
			if (num.compareAndSet(number, number - 1)) {
				if (number - 1 == 0) {
					balance = 0;
					return balan;
				}
				long average = balan / number;
				long result = ThreadLocalRandom.current().nextLong(1, average * 2);
				balance -= result;
			}
			return -1;
		}
	}
### Java 中 `synchronized` 关键字的使用场景与原理 #### 一、`synchronized` 的作用 `synchronized` 是 Java 提供的一种内置锁机制,用于控制多线程环境下对共享资源的竞争访问。通过加锁的方式,它可以确保同一时刻只有一个线程可以执行被保护的代码区域,从而保障数据的一致性和完整性[^1]。 --- #### 二、`synchronized` 的使用方式 `synchronized` 支持两种主要的使用形式: 1. **同步方法** 当将 `synchronized` 应用于实例方法时,该方法会自动获取当前对象的对象级锁(即 `this`)。其他线程如果尝试调用同一个对象上的任何 `synchronized` 方法,则会被阻塞直到锁释放。 ```java public class Counter { private int count; // 同步方法 public synchronized void increment() { count++; } } ``` 这种方式适用于整个方法都需要保持原子性的场景[^1]。 2. **同步代码块** 如果只需要对部分代码进行同步操作,可以通过 `synchronized` 块指定一个特定的对象作为锁来减少锁定范围。这种方式更加灵活且高效。 ```java public class SharedResource { private final Object lock = new Object(); private int value; public void updateValue(int newValue) { synchronized (lock) { // 明确指定了锁对象 this.value = newValue; } } } ``` 此外,在静态方法中也可以应用 `synchronized`,此时使用的锁是类级别的锁(即 `Class` 对象),而不是某个具体实例的锁[^2]。 --- #### 三、`synchronized` 的底层实现 在 JVM 层面,`synchronized` 的实现依赖于 Monitor 和其内部的数据结构——ObjectMonitor。当线程进入由 `synchronized` 守护的临界区时,JVM 需要完成以下几个动作: - 获取目标对象的监视器(Monitor)。 - 将线程加入等待队列并测试是否有可用的锁。 - 成功获得锁后允许继续运行;否则挂起直至条件满足。 为了提高性能表现,现代 JDK 实现了多种优化策略,其中包括但不限于轻量级锁、偏向锁以及重量级锁之间的动态转换过程,这一系列技术统称为 *锁升级* 或者说是自适应锁膨胀机制[^2]。 --- #### 四、锁的状态变迁(锁升级) 初始状态下,默认采用的是无竞争模式下的最简单形态—**偏向锁**。一旦检测到有多个线程频繁争夺相同资源,则逐步提升至更高级别的状态以应对复杂情况: 1. **偏向锁**: 初始默认设置,假设只存在单一线程持续持有某把锁的情况,因此无需实际争抢即可快速复用已有权限; 2. **轻量级锁**: 若发现不同线程交替请求同一件事物,则切换至此阶段利用 CAS 操作代替传统互斥算法降低开销; 3. **重量级锁**: 在极端条件下发生激烈冲突时退化成操作系统层面的传统信号量处理手段。 这种分层设计有效平衡了效率与公平性之间矛盾关系的同时也极大提升了程序整体吞吐能力。 --- ### 总结 综上所述,`synchronized` 不仅提供了便捷易懂的方法帮助开发者构建安全可靠的并发应用程序,而且经过多次迭代改进之后已经成为一种既强大又高效的解决方案之一。无论是初学者还是资深工程师都应该熟练掌握它的基本概念及相关技巧以便更好地解决现实世界中的各种挑战。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值