aqs全称抽象队列同步器是AbstractQueuedSynchronize抽象类。
它是一个用来构建锁和同步器的框架,它底层用了CAS技术来保证操作的原子性,同时利用FIFO队列实现线程间的锁竞争,将基础的同步相关抽象细节放在AQS,它能够成为实现大部分同步需求的基础,也是JUC并发包同步的核心基础组件。Lock
、ReadWriteLock
、CountDowndLatch
、CyclicBarrier
、Semaphore
、ThreadPoolExecutor
等都是在AQS的基础上实现的。
如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。
AQS使用一个volatile的state变量来表示同步状态,通过内置的FIFO队列来完成获取资源线程的排队工作。AQS使用CAS对该同步状态进行原子操作实现对其值的修改。
1、执行tryAcquire方法尝试加锁
2、如果判断当前线程如果是自己,则把state+1,加锁成功。如果不是继续判断当前锁是否空闲(state是否为0),是的话通过cas加锁,返回加锁结果(由子类实现)
3、如果加锁失败,则把当前线程封装进Node对象中,把Node.prev指向tail组成链表结构,如果线程需要阻塞则执行LockSupport.park(this);
1、执行tryRelease方法释放锁
2、State进行-1操作,如果是0,则把当前线程ExclusiveOwnerThread置为null。否则设置State-1值
3、如果有等待的线程,则使用LockSupport.unpark唤醒包装了Node的下一个节点的线程
主要包含4个字段
aqs分为两种“独占锁”和”共享锁“
独占锁又分为“公平锁”和“非公平锁”。
CountDownLatch中count down是倒数的意思,latch则是门闩的含义。整体含义可以理解为倒数的门栓,似乎有一点“三二一,开门后所有线程一起跑”的感觉。
使用方法
首先建立CountDownLatch对象,并且传入参数即为count初始值。如果一个线程调用了await()方法,那么这个线程便进入阻塞状态,并进入阻塞队列。如果一个线程调用了countDown()方法,则会使count-1;当count的值为0时,这时候阻塞队列中调用await()方法的线程便会逐个被唤醒,从而进入后续的操作。
常用方法有:
使用场景
Semaphore(信号量)
可以通过其限制执行的线程数量(同时访问共享资源的线程数),达到限流的效果。
使用方法
当一个线程执行时先通过其方法进行获取许可操作,获取到许可的线程继续执行业务逻辑,当线程执行完成后进行释放许可操作,未获取达到许可的线程进行等待或者直接结束。
常用方法有:
使用场景
CyclicBarrier字面意思是“可重复使用的栅栏”,CyclicBarrier 相比 CountDownLatch 来说,要简单很多,它是 ReentrantLock 和 Condition 的组合使用,只是 CyclicBarrier 可以有不止一个栅栏,因为它的栅栏(Barrier)可以重复使用(Cyclic)。
利用CyclicBarrier类可以实现一组线程相互等待,当所有线程都到达某个屏障点后再进行后续的操作。
使用方法
常用方法有:
使用场景
与CountDownLatch区别
CountDownLatch是一个同步的辅助类,允许一个或多个线程,等待其他一组线程完成操作,再继续执行。
CyclicBarrier是一个同步的辅助类,允许一组线程相互之间等待,达到一个共同点,再继续执行。
区别:
Phaser允许并发多阶段任务。Phaser类机制是在每一步结束的位置对线程进行同步,当所有的线程都完成了这一步,才允许执行下一步。
使用场景
用来解决控制多个线程分阶段共同完成任务的情景问题。
Exchanger用于线程间进行通信、数据交换。Exchanger提供了一个同步点exchange方法,两个线程调用exchange方法时,无论调用时间先后,两个线程会互相等到线程到达exchange方法调用点,此时两个线程可以交换数据,将本线程产出数据传递给对方。
参考:【java并发编程】lock接口_现实、太残忍的博客-CSDN博客
参考:java使用jdk的ThreadPoolExecutor线程池_现实、太残忍的博客-CSDN博客