一. AQS原理:
在AQS内部会保存一个状态变量state,通过CAS修改该变量的值,修改成功的线程表示获取到该锁,没有修改成功,或者发现状态state已经是加锁状态,则通过一个Waiter对象封装线程,添加到等待队列中,并挂起等待被唤醒。
AQS维护了一个volatile int state(代表共享资源)和一个FIFO线程等待队列(多线程争用资源被阻塞时会进入此队列)。
原理图如下:
二.AQS定义两种资源共享方式:
-
Exclusive(独占):只有一个线程能执行,如ReentrantLock。又可分为公平锁和非公平锁:
公平锁:按照线程在队列中的排队顺序,先到者先拿到锁 非公平锁:当线程要获取锁时,无视队列顺序直接去抢锁,谁抢到就是谁的
-
Share(共享):多个线程可同时执行,如Semaphore/CountDownLatch。Semaphore、CountDownLatch、 CyclicBarrier、ReadWriteLock .
三. 公平锁和非公平锁有哪些区别?
公平锁指的是线程获取锁的顺序是按照加锁顺序来的,而非公平锁指的是抢锁机制,先 lock() 的线程不一定先获得锁。
四. ReentrantLock
- ReentrantLock是基于AQS实现的
五.AQS的原理图-枷锁过程:
- 枷锁过程描述:开始state==0
- 线程1 和线程2 同时通过CAS枷锁,这时候只有一个线程能成功
- 假设线程1成功,线程2失败,这时state=1,有个变量枷锁线=线程1
- 线程2加入等待队列中,等待线程1释放锁,线程1唤醒线程2,重新执行枷锁
六.AQS 组件总结:
- Semaphore(信号量)-允许多个线程同时访问: synchronized 和 ReentrantLock 都是一次只允许一个线程访问某个资源,Semaphore(信号量)可以指定多个线程同时访问某个资源。
- CountDownLatch (倒计时器): CountDownLatch是一个同步工具类,用来协调多个线程之间的同步。这个工具通常用来控制线程等待,它可以让某一个线程等待直到倒计时结束,再开始执行。
- CyclicBarrier(循环栅栏): CyclicBarrier 和 CountDownLatch 非常类似,它也可以实现线程间的技术等待,但是它的功能比 CountDownLatch 更加复杂和强大。主要应用场景和 CountDownLatch 类似。CyclicBarrier 的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。CyclicBarrier默认的构造方法是 CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await()方法告诉 CyclicBarrier 我已经到达了屏障,然后当前线程被阻塞。
七.组件代码示例:
- CountDownLatch(倒计时器):个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待 ,即CountDownLatch允许一个或多个线程等待其他线程完成操作
public class CountDownLatchExample1 {
private final static int threadCount= 200;
public static void main(String[] args) throws Exception{
ExecutorService exec = Executors.newCachedThreadPool();
final CountDownLatch countDownLatch = new CountDownLatch(threadCount);
for(int i=0; i<threadCount; i++){
final int threadNum = i;
exec.execute(() ->{
try {
test(threadNum);
} catch (Exception e) {
log.error("exception 为 {}",e);
}finally {
countDownLatch.countDown();//每次都是减1
}
});
}
countDownLatch.await();//保证上面的200线程都是执行完的
//第一个参数代表等待的时间,第二个参数:时间的单位,超过10毫秒就执行
countDownLatch.await(10, TimeUnit.MICROSECONDS);
log.info("finish");
}
public static void test(int threadNum) throws Exception{
Thread.sleep(100);
log.info("threadNum === {}",threadNum);
Thread.sleep(100);
}
}
- Semaphore(信号量):控制某个资源被同时访问的个数(控制并发的数量),例如:使用的场景:只能提供有限访问的资源,例如:mysql仅提供同一时刻20个资源访问.
public class SemaphoreExample1 {
private final static int threadCount= 20;
public static void main(String[] args) throws Exception{
ExecutorService exec = Executors.newCachedThreadPool();
//同时最多允许3个线程访问
final Semaphore semaphore = new Semaphore(3);
for(int i=0; i<threadCount; i++){
final int threadNum = i;
exec.execute(() ->{
try {
//获取 一个 许可
semaphore.acquire();
//获取多个许可
//semaphore.acquire(3);
semaphore.tryAcquire();//尝试获取许可
test(threadNum);//最多有三个线程同时执行test()方法
//释放许可
semaphore.release();
//释放多个许可
//semaphore.release(3);
} catch (Exception e) {
log.error("exception 为 {}",e);
}
});
}
log.info("finish");
}
public static void test(int threadNum) throws Exception{
log.info("threadNum === {}",threadNum);
Thread.sleep(100);
}
}
- CyclicBarrier(循环栅栏):是每个线程相互等待,等到达到设定的计数器时候,所有的线程在执行
public class CyclicBarrierExample1 {
//5个线程相互等待,到五个线程准备好时候在执行
private static CyclicBarrier barrier = new CyclicBarrier(5);
public static void main(String[] args) throws Exception{
Map map;
HashMap hashMap = new HashMap();
hashMap.put("key", "value");
ConcurrentHashMap concurrentHashMap = new ConcurrentHashMap();
concurrentHashMap.put("hashmap", "hashmap");
concurrentHashMap.get("hashmap");
//这个参数就是核心线程池的数量
ExecutorService executorService = Executors.newFixedThreadPool(10);
ExecutorService exec = Executors.newCachedThreadPool();
for (int i=0;i<10;i++){
int threadNum = i;
Thread.sleep(1000);
exec.execute(() ->{
try {
race(threadNum);
} catch (Exception e) {
e.printStackTrace();
log.info("exception {}",e);
}
});
}
}
public static void race(int num) throws Exception{
Thread.sleep(1000);
log.info(" {}is ready",num);
barrier.await();//等待达到五个线程,在执行
log.info("{} continue",num);
}
}