深入探秘CAS、Synchronized和ReentrantLock:并发的妙钥
2023-12-24 09:07:41
在现代计算机系统中,并发编程已经成为不可或缺的一部分。为了协调多线程并发执行,Java提供了多种同步机制,其中CAS、Synchronized和ReentrantLock是广受欢迎的选择。在这篇文章中,我们将深入探究这些锁的原理和实现,帮助您更好地理解Java并发编程的精髓。
一、锁相关概念
在并发编程中,锁是一种重要的同步机制,用于控制对共享资源的访问。锁可以防止多个线程同时访问共享资源,从而避免数据损坏和程序崩溃。
Java中提供了两种类型的锁:
- 内置锁(Intrinsic Lock): 也称为监视器锁,是Java语言内置的锁。使用synchronized可以获取内置锁。内置锁是重量级锁,开销较大。
- 可重入锁(Reentrant Lock): 是一种重量级锁,可以被同一个线程多次获取。可重入锁通常用于构建更复杂的同步结构,如读写锁和条件变量。
二、Synchronized的一些优化
为了减少内置锁的开销,Java在JDK 1.6中引入了一些优化技术:
- 偏向锁(Biased Locking): 当一个线程第一次获取锁时,JVM会将锁标记为偏向锁。偏向锁只允许获取锁的线程访问共享资源,其他线程无法获取锁。当其他线程试图获取锁时,偏向锁会被撤销,锁会恢复为轻量级锁或重量级锁。
- 轻量级锁(Lightweight Locking): 当一个线程第一次获取锁时,JVM会将锁标记为轻量级锁。轻量级锁使用CAS操作来获取锁。如果CAS操作成功,则该线程获取锁;如果CAS操作失败,则锁会升级为重量级锁。
- 自旋锁(Spinning): 当一个线程无法获取锁时,它会不断地尝试获取锁。这种技术称为自旋。自旋可以减少线程之间的上下文切换开销,提高程序性能。
三、CAS实现(以AtomicInteger为例)
CAS(Compare-And-Swap)是一种硬件指令,可以原子地比较和交换内存中的值。CAS操作通常用于构建无锁数据结构,如原子变量。
AtomicInteger是Java中内置的原子变量类,它使用CAS操作来实现原子更新。AtomicInteger的compareAndSet方法可以原子地比较和交换变量的值。如果变量的值与预期值相等,则CAS操作成功,变量的值被更新为新值;否则,CAS操作失败,变量的值保持不变。
public class AtomicInteger {
private volatile int value;
public boolean compareAndSet(int expectedValue, int newValue) {
return UNSAFE.compareAndSwapInt(this, valueOffset, expectedValue, newValue);
}
}
四、ReentrantLock实现原理
ReentrantLock是Java中内置的可重入锁。ReentrantLock使用AQS(AbstractQueuedSynchronizer)来实现锁的功能。AQS是一个抽象类,它提供了获取锁、释放锁、尝试获取锁等基本操作。
ReentrantLock的实现原理如下:
- 当一个线程第一次获取锁时,它会创建一个队列节点,并将该节点加入到锁的等待队列中。
- 当锁可用时,队列中的第一个节点会获取锁,并将其状态标记为已获取锁。
- 当一个线程释放锁时,它会将锁的状态标记为未获取锁,并唤醒等待队列中的下一个节点。
五、AQS目录一、锁相关概念
AQS(AbstractQueuedSynchronizer)是一个抽象类,它提供了获取锁、释放锁、尝试获取锁等基本操作。AQS是Java中许多锁的基础类,如ReentrantLock、Semaphore和CountDownLatch。
AQS的实现原理如下:
- AQS维护一个队列,用于存储等待获取锁的线程。
- 当一个线程获取锁时,它会将自己加入到队列中,并将其状态标记为已获取锁。
- 当一个线程释放锁时,它会将锁的状态标记为未获取锁,并唤醒队列中的下一个节点。
六、结语
CAS、Synchronized和ReentrantLock是Java中三种常用的锁。这些锁各有优缺点,在不同的场景下使用不同的锁可以提高程序性能。
希望这篇文章能够帮助您更好地理解Java并发编程的精髓,轻松驾驭多线程开发。