共计 2918 个字符,预计需要花费 8 分钟才能阅读完成。
AbstractQueuedSynchronizer
今天在b站看了关于 AbstractQueuedSynchronizer 的合集,稍微了解了一下 AbstractQueuedSynchronizer 的工作原理,简单记录一下。
AbstractQueuedSynchronizer (AQS) 是 Java 中 java.util.concurrent
包的一部分,是一个基础框架类,用于实现锁和同步器(如 ReentrantLock、Semaphore、CountDownLatch 等)。
AQS 是一个抽象类,主要为实现同步器提供了以下关键功能:
-
同步状态的管理:
- AQS 使用一个
int
类型的变量(state
)来表示同步状态。(该变量使用 volatile 修饰) - 提供了
getState
、setState
和compareAndSetState
方法,允许子类安全地修改同步状态。
- AQS 使用一个
-
线程队列:
- AQS 使用一个先进先出的等待队列来管理被阻塞的线程。
- 当线程尝试获取同步资源失败时,会被加入这个等待队列。(需要注意的是,不是获取锁失败马上就加入阻塞队列,而是中间有很多次获取锁的机会)
-
独占模式和共享模式:
- 独占模式(Exclusive Mode):一个线程独占资源,例如
ReentrantLock
。 - 共享模式(Shared Mode):多个线程可以共享资源,例如
Semaphore
和CountDownLatch
。
- 独占模式(Exclusive Mode):一个线程独占资源,例如
-
模板方法模式:
AQS 定义了一些模板方法(如
tryAcquire
、tryRelease
、tryAcquireShared
和tryReleaseShared
),需要子类去实现它们以完成具体的同步逻辑。boolean tryAcquire(int arg)
: 尝试以独占模式获取资源,由子类实现。boolean tryRelease(int arg)
: 尝试以独占模式释放资源,由子类实现。int tryAcquireShared(int arg)
: 尝试以共享模式获取资源,由子类实现。boolean tryReleaseShared(int arg)
: 尝试以共享模式释放资源,由子类实现。boolean isHeldExclusively()
: 判断当前同步器是否被某线程独占。
关于为什么这些方法并非都是抽象方法,而是普通方法,是希望子类按需实现,而不是都需要把这些方法实现。
ReentrantLock加锁流程
下图是 ReentrantLock 加锁的流程图:
LockSupport阻塞和唤醒
上图中线程阻塞和唤醒是通过 LockSupport
实现的。LockSupport.park()
和 LockSupport.unpark(Thread thread)
是 Java 中提供的低级线程阻塞和唤醒机制。它们是 java.util.concurrent.locks.LockSupport 类的一部分,用于实现线程的挂起和唤醒操作。
-
park()
:- 将当前线程阻塞(挂起),直到其他线程调用
unpark(Thread thread)
或线程被中断。 - 是一个相对低级的阻塞操作,用于替代
Thread.suspend()
的不安全行为。 - 它允许线程在等待某个条件满足时挂起,而不消耗 CPU 资源。
- 将当前线程阻塞(挂起),直到其他线程调用
-
unpark(Thread thread)
:- 唤醒指定线程,使其从
park()
状态恢复。 - 如果被唤醒的线程当前未被阻塞在
park()
,调用unpark()
会记录一个许可,下次调用park()
时立即返回。
- 唤醒指定线程,使其从
假设有一下代码,想要按顺序打印 ABC,但是由于线程执行是不固定的,所以每次执行会出现不同的结果。
public class LockSupportTest {
public static void main(String[] args) {
LockSupportTest lockSupportTest = new LockSupportTest();
Thread t1 = new Thread(() -> lockSupportTest.printA());
Thread t2 = new Thread(() -> lockSupportTest.printB());
Thread t3 = new Thread(() -> lockSupportTest.printC());
t1.start();
t2.start();
t3.start();
}
private void printA(){
try {
Thread.sleep(5);
System.out.print("A");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
private void printB(){
try {
Thread.sleep(5);
System.out.print("B");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
private void printC(){
try {
Thread.sleep(5);
System.out.print("C");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
此时使用 LockSupport
改造一下代码即可稳定顺序打印 ABC:
public class LockSupportTest {
public static void main(String[] args) {
LockSupportTest lockSupportTest = new LockSupportTest();
Thread t3 = new Thread(() -> lockSupportTest.printC());
Thread t2 = new Thread(() -> lockSupportTest.printB(t3));
Thread t1 = new Thread(() -> lockSupportTest.printA(t2));
t1.start();
t2.start();
t3.start();
}
private void printA(Thread thread){
try {
Thread.sleep(5);
System.out.print("A");
LockSupport.unpark(thread);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
private void printB(Thread thread){
try {
Thread.sleep(5);
LockSupport.park();
System.out.print("B");
LockSupport.unpark(thread);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
private void printC(){
try {
Thread.sleep(5);
LockSupport.park();
System.out.print("C");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}