AQS
AQS ( Abstract Queued Synchronizer )是一个抽象的队列同步器,通过维护一个共享资源状态( Volatile Int State )和一个先进先出( FIFO )的线程等待队列来实现一个多线程访问共享资源的同步框架。
AQS 定义了两种资源共享方式:
1.Exclusive:独占,只有一个线程能执行,如ReentrantLock
2.Share:共享,多个线程可以同时执行,如Semaphore、CountDownLatch、CyclicBarrier
重入锁ReentrantLock
定义:一个持有锁的线程,在释放锁之前,如果再次访问加了该同步锁的其他方法,这个线程不需要去争抢锁,只需要记录其重入次数。默认使用sync的不公平锁NonfairSync实现。
实现图解:
这里需要注意的是,由于ReentranLock默认基于NoFairSync实现,所以在线程队列中的线程被唤醒时,不会直接获取到锁,而是需要和外部进入的新线程去争抢锁。当然,新的线程如果没有抢到锁,也会进入线程队列等待顺序唤醒。
倒计时 CountDownLatch
定义:允许一个或多个线程一直等待,直到其他线程的操作执行完毕在执行。
初始化源码
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
由其初始化方式,可以看出,countDownLatch的本质是直接给AQS的state赋值,后面必须要执行对应个数的释放锁操作,才能使相应的线程继续操作。
使用图解
当然,如果执行的线程次数不够时,会导致阻塞,无法进行唤醒。
限流Semaphore
定义:译为信号量,可以控制同时访问的线程个数,通过acquire()获得一个许可,如果没有就进入等待队列;通过release释放一个许可,唤醒全部等待的线程去抢夺锁。类似于限流;
CylicBarrier(可循环使用的屏障)
定义:让一组线程到达一个屏障时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续工作。其功能类似于CountDownLatch;不同的是,CountDownLatch是基于
使用场景:存在需要所有子任务都完成后才执行主任务。
当线程数与parties(循环次数)不一致时,所有线程都会被阻塞;解决方法:
①设定await(timeout),超时退出等待
②通过cyclicBarrier.rest重置计数器,抛出异常进行处理。
实现原理:CylicBarrier是基于AQS中的ReentrantLock和Condition实现。值得注意的是,在执行barrierAction时,调用的是run()方法而不是start。
start() 方法用于启动或开始执行新创建的线程。当调用 start() 方法时,会创建一个新线程,并且这个新创建的线程会执行保存在 run() 方法中的任务。只能调用一次 start() 方法。
run() 方法用于启动或开始执行同一个线程。当调用 run() 方法时,不会像 start() 方法那样创建新线程。该方法由当前线程执行。可以多次调用。
独占式和共享式锁唤醒线程队列的不同
独占式:只唤醒head下的nextNode
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
共享式:通过自旋锁,唤醒队列中除了head以外全部的Node(因为head实际上只做一个标志,并没有关联实际线程)
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {//通过unparkSuccessor唤醒节点,直到h=head
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
Condition条件
- Condition是一个多线程协调通信的工具类,可以让某些线程一起等待某个条件,当条件被满足时,线程才会被唤醒。其原理是通过AQS等待队列及condition队列实现等待唤醒