【深入理解同步器AQS】

简介: 【深入理解同步器AQS】

AQS(AbstractQueuedSynchronizer)是Java中提供的一个同步器框架,可以用来实现各种同步工具,如ReentrantLock、Semaphore、CountDownLatch等。本文将从原理、实现方式、应用场景以及代码实现等方面来深入理解AQS。

1. AQS原理

AQS通过一个FIFO队列来管理线程的竞争,队列中的每个节点都代表一个等待线程,当某个线程请求共享资源时,如果发现有其他线程已经占用了这个资源,它就会被放到队列的尾部等待;当占用资源的线程释放资源时,它会通知队列的头部节点,使它可以重新竞争资源。整个过程就是一个典型的“先进先出”的同步机制。

AQS的核心是一个volatile变量state,通过对state的操作来判断当前线程是否可以获得共享资源。当state为0时,表示当前没有线程占用资源,其他线程可以通过compareAndSetState()方法来竞争资源;当state为1时,表示当前已有线程占用资源,其他线程必须进入队列等待。

此外,AQS还提供了两个方法acquire()和release()来实现单线程对共享资源的获取和释放。

2. AQS实现方式

AQS的实现方式主要包括三个部分:同步状态的管理、等待队列的管理以及线程的阻塞与唤醒。

2.1 同步状态的管理

AQS通过一个volatile变量state来管理同步状态。在AQS中,state代表的是资源的数量,一般情况下,state等于0表示资源没有被占用,大于0表示资源已经被占用。在使用AQS实现同步器时,我们需要根据具体使用场景来赋予state合适的含义。

AQS提供了compareAndSetState()方法来对state进行原子操作,保证并发下的操作的正确性。compareAndSetState()方法的实现代码如下:

protected final boolean compareAndSetState(int expect, int update) {
    // see below for intrinsics setup to support this
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long stateOffset;
static {
    try {
        stateOffset = unsafe.objectFieldOffset
            (AbstractQueuedSynchronizer.class.getDeclaredField("state"));
    } catch (Exception ex) { throw new Error(ex); }
}

在上面的代码中,我们可以看到,compareAndSetState()方法实际上是通过调用sun.misc.Unsafe类的compareAndSwapInt()方法来实现的。

2.2 等待队列的管理

AQS中的等待队列是一个FIFO队列,可以通过同步器提供的acquire()方法向队列中添加一个节点,也可以通过release()方法将头节点出队,唤醒等待队列中的下一个节点。AQS中等待队列的实现是通过一个双向链表来实现的,每个节点都有前驱节点和后继节点。

在AQS中,队列中的每个节点都是一个ConditionObject对象,它是AQS的内部类,继承了Condition接口。ConditionObject实现了Condition接口的所有方法,它能够使线程以安全的方式等待特定的条件。

2.3 线程的阻塞与唤醒

当线程请求共享资源时,如果发现资源已被占用,线程就会被阻塞,并被加入到等待队列中。线程被阻塞后,它就会进入WAITING状态。

在AQS中,线程的阻塞与唤醒是通过LockSupport类来实现的。LockSupport是Java中用于线程阻塞与唤醒的工具类,它提供了park()和unpark()方法来控制线程的阻塞和唤醒。

3. AQS应用场景

AQS广泛应用于Java并发包中的各种同步器,如ReentrantLock、Semaphore、CountDownLatch等。它为我们提供了一种可扩展、可重用的同步机制,使我们能够方便地实现各种同步工具。

4. AQS代码实现

下面我们以ReentrantLock为例,演示如何使用AQS实现一个可重入的互斥锁。

Lock接口是Java并发包中提供的一个用于互斥同步的接口,它定义了lock()、unlock()、tryLock()等方法。在AQS中,我们可以通过继承AbstractQueuedSynchronizer类来实现Lock接口的所有方法。

public class ReentrantLock implements Lock, java.io.Serializable {
    private final Sync sync;
    abstract static class Sync extends AbstractQueuedSynchronizer {
        abstract void lock();
        // 其他方法略
    }
    static final class NonfairSync extends Sync {
        // 具体实现略
    }
    static final class FairSync extends Sync {
        // 具体实现略
    }
}

在上面的代码中,我们可以看到,ReentrantLock中定义了一个Sync类,它继承了AbstractQueuedSynchronizer,Sync类中定义了lock()、unlock()等方法,这些方法实际上是通过调用AQS的acquire()、release()等方法来实现的。

为了提高性能,ReentrantLock还提供了两种Sync的实现方式:NonfairSync和FairSync。NonfairSync采用非公平锁的方式,即线程可以随时争夺锁,无需排队;而FairSync则采用公平锁的方式,即线程按照先来先服务的原则进行排队争夺锁。在ReentrantLock的构造函数中,我们可以根据需要来选择使用哪种Sync的实现方式。

最后,我们来看一下ReentrantLock的lock()方法的实现:

public void lock() {
    sync.acquire(1);
}
public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

在lock()方法中,我们调用了Sync的acquire()方法来获取锁。在acquire()方法中,我们首先调用tryAcquire()方法尝试获取锁,如果获取成功,直接返回;否则,将当前线程加入到等待队列中,并阻塞当前线程。当其他线程释放锁时,acquireQueued()方法会对等待队列中的下一个节点进行唤醒,让它重新尝试获取锁。

总结

通过上述分析,我们可以发现,AQS是Java中用于实现同步器的一个重要框架。借助AQS,我们可以方便地实现各种同步工具,如ReentrantLock、Semaphore、CountDownLatch等,为并发编程提供了强有力的支持。

小故事

从前有个村子,村子里的人们每天都要从家里到集市上买卖东西。为了让大家更有规律地出门,村子里决定修建一座门,每天早上门口一打开,大家就可以出门了。但是,每天出门的人太多了,门口经常会拥堵,导致一些人出门需要等待很长时间。于是,村子里的人又决定在门口添加一个能够控制出门的机制,这个机制能够很好地控制出门的人数,让大家更有规律地出门。

这个门口的机制就像是同步器AQS的底层工作原理。AQS其实就是一种锁的机制,它可以让代码块在并发环境中互斥地执行,避免竞争问题,并提高并发性能。

在同步器AQS的底层实现中,比如ReentrantLock,就是通过“门”的模式进行控制。首先,AQS会维护一个state状态值,它代表着当前的锁状态。当一个线程想要进入同步代码块时,它就需要先尝试去获取锁。如果此时state值为0,那么线程就可以获取到锁,并将state值设置为1,表示它已经拿到了锁。

但是,如果此时state值不为0,那么线程就不能获取到锁,它就需要进入等待状态,等待其他线程释放锁。等待的线程会被放到同步队列中,等待队列中的线程都会被阻塞,直到某个线程释放了锁,才会有一个线程从等待队列中被唤醒并进入同步代码块执行。

这个“门”的机制就是同步器AQS的底层工作原理,通过控制进入同步代码块的线程数,避免竞争问题,让代码块在并发环境中互斥地执行,提高并发性能。


相关文章
|
人工智能 算法
同步器的介绍
一、同步器的基本原理 同步器是一种电子电路,用于同的电子信号进行同步。它的基本原理是根据输入信号的特征,通过适当的控制和调节,使输出信号与输入信号保持同步。同步器通常由触发器、计数器、时钟等组成,通过这些元件的协同工作,实现信号的同步和精确控制。 二、同步器的应用领域 同步器在各个领域都有广泛的应用。在通信领域,同步器用于确保数据传输的准确性和稳定性。在计算机领域,同步器用于控制和同步各个部件的工作,保证计算机系统的正常运行。在音视频领域,同步器用于音频和视频信号的同步播放,提供更好的观看和听觉体验。在工业自动化领域,同步器用于控制和同步各个机械设备的运行,提高生产效率和精确度。 三、同步器的
187 0
|
7月前
|
安全 Java
利用AQS(AbstractQueuedSynchronizer)实现一个线程同步器
利用AQS(AbstractQueuedSynchronizer)实现一个线程同步器
|
7月前
【1】请问什么是 AQS?
【1】请问什么是 AQS?
62 0
|
Java C++
什么是AQS?
AQS(AbstractQueuedSynchronizer)是Java中的一个同步器框架
446 1
|
算法 Java
了解AQS
了解AQS
89 0
|
计算机视觉
AQS
AQS
88 0
|
存储 安全
AQS
一、为什么需要AQS?以及AQS的作用和重要性? AQS(AbstractQueuedSynchronizer)的重要性 AQS被用在ReentrantLock、ReentrantReadWriteLock、Semaphore、CountDownLatch、ThreadPoolExcutor的Worker中都有运用(JDK1.8)。AQS是这些类的底层原理,JUC包里很多重要的工具类背后都离不开AQS框架。
135 0
|
设计模式 安全 Java
【AQS】
【AQS】
124 0
【AQS】
|
算法 Java Linux
AQS这样学就很简单了
哈喽,我是子牙。十余年技术生涯,一路披荆斩棘从技术小白到技术总监到JVM专家到创业。技术栈如汇编、C语言、C++、Windows内核、Linux内核。特别喜欢研究虚拟机底层实现,对JVM有深入研究。分享的文章偏硬核,很硬的那种。
126 0
AQS这样学就很简单了