返回

深入探秘CAS、Synchronized和ReentrantLock:并发的妙钥

Android

在现代计算机系统中,并发编程已经成为不可或缺的一部分。为了协调多线程并发执行,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的实现原理如下:

  1. 当一个线程第一次获取锁时,它会创建一个队列节点,并将该节点加入到锁的等待队列中。
  2. 当锁可用时,队列中的第一个节点会获取锁,并将其状态标记为已获取锁。
  3. 当一个线程释放锁时,它会将锁的状态标记为未获取锁,并唤醒等待队列中的下一个节点。

五、AQS目录一、锁相关概念

AQS(AbstractQueuedSynchronizer)是一个抽象类,它提供了获取锁、释放锁、尝试获取锁等基本操作。AQS是Java中许多锁的基础类,如ReentrantLock、Semaphore和CountDownLatch。

AQS的实现原理如下:

  1. AQS维护一个队列,用于存储等待获取锁的线程。
  2. 当一个线程获取锁时,它会将自己加入到队列中,并将其状态标记为已获取锁。
  3. 当一个线程释放锁时,它会将锁的状态标记为未获取锁,并唤醒队列中的下一个节点。

六、结语

CAS、Synchronized和ReentrantLock是Java中三种常用的锁。这些锁各有优缺点,在不同的场景下使用不同的锁可以提高程序性能。

希望这篇文章能够帮助您更好地理解Java并发编程的精髓,轻松驾驭多线程开发。