- CountDownLatch
- 介绍与用法
- 原理和Api
- CyclicBarrier
- 介绍与用法
- 原理和Api
- Semaphore
- 介绍与用法
- 原理和Api
1. CountDownLatch
(1)介绍
- 也称 倒计时锁 / 闭锁,用来进行线程同步协作,让一个线程等待其他所有线程完成倒计时后再恢复运行
- 可以理解为加强版join(),等待n个线程执行完,再恢复运行
(2)用法
- 构造器: CountDownLatch latch = new CountDownLatch(n),意为等待n个线程执行完,再恢复运行
- 需要被恢复的线程执行await(),其他线程执行完后countDown()
- 这里主线程调用:latch.await(),类似join()等待
- 其他线程执行:latch.countDown()
public static void main(String[] args) throws InterruptedException {
//创建
CountDownLatch latch = new CountDownLatch(2);
//执行thread0
Thread thread0 = new Thread(()->{
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread() + " thread0 start...");
//会让latch的计数器-1;
latch.countDown();
System.out.println(Thread.currentThread() +" thread0 end...");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
//执行thread1
Thread thread1 = new Thread(()->{
try {
Thread.sleep(2000);
System.out.println(Thread.currentThread() + " thread1 start...");
//会让latch的计数器-1;
latch.countDown();
System.out.println(Thread.currentThread() + " thread1 end...");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread0.start();
thread1.start();
System.out.println(Thread.currentThread() +" main thread start...");
//进入waiting一直等待到有2个线程结束后再恢复
latch.await();
System.out.println(Thread.currentThread() + " main thread end...");
}
- 运行结果:主线程执行到 latch.await()后被挂起,直到有2个线程都执行到了latch.countDown()后,主线程继续进入RUNNING态
(3)原理
- 静态内部类Sync继承AQS
- 不同于ReentrantLock,该Sync为共享锁,state变量记录共享模式的个数(初始值为构造器参数count)
- Sync内部重写了tryAcquireShare()和tryReleaseShared()方法
- 每次子线程执行完毕调用countDownLatch.countDown()方法后让count-1,count=0后主线程才返回
private static final class Sync extends AbstractQueuedSynchronizer {
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
protected boolean tryReleaseShared(int releases) {
for (;;) {
int c = getState();
if (c == 0) return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
(4)Api
void await() | 委托Sync调用AQS的doAcquireShareInterruptibly()方法阻塞当前线程; 实际还是调用自己重写的tryAcquireShared()方法 两种情况会恢复运行态: (1)其他线程调用countDown()方法,使得计数器为0 (2)其他线程调用interrupt(),该线程被打断,抛出InterruptedException异常 |
boolean await(long, TimeUnit) | 委托Sync调用AQS的doAcquireShareNanos(1, unit.toNanos(timeout))带超时的方法阻塞当前线程; 实际还是调用自己重写的tryAcquireShared()方法 三种情况恢复运行态: (1)其他线程调用countDown()方法,使得计数器为0,返回true (2)其他线程调用interrupt(),该线程被打断,抛出InterruptedException异常 (3)时间到,返回false |
void countDown() | 委托Sync调用AQS的releaseShared()方法 实际还是调用自己重写的tryReleaseShared()方法 用for(;;)循环进行CAS操作,更新state值减1; 当state减1等于0后就唤醒被阻塞的线程 |
long getCount() | 获取state的值 |
2. CyclicBarrier
(1)介绍
- 也称 回环屏障
- 之前CountDownLatch的计数器是一次性的,用完就结束了,count的值不能重置
- 而CyclicBarrier每次count=0后,会重置回原来的参数(构造器传入的那个参数)
(2)用法
- 构造器
- CyclicBarrier cyc = new CyclicBarrier(n)
- CyclicBarrier cyc = new CyclicBarrier(n, new Runnable(()->{...}))
- 其他线程调用:cyc.await();
(3)原理
- 内部属性 ReentrantLock:独占锁实现
- 内部属性 parties:记录线程个数,final类型恒定不变
- 内部属性 count:一开始count=parties,每次线程调用await()方法后count减1,当count=0后唤醒所有线程,然后重置count=parties
(4)Api
int await() | 调用该方法的线程会阻塞 三种情况恢复运行态: (1)有n个线程都调用了await()方法,所有线程都恢复运行 (2)其他线程调用interrupt()方法 (3)与当前屏障点关联的 Generation对象的 broken 标志被设置为 true 时 |
boolean await(long, TimeUnit) | 四种情况恢复运行态: (1)有n个线程都调用了await()方法,所有线程都恢复运行,这时返回true (2)超时,返回false (2)其他线程调用interrupt()方法 (3)与当前屏障点关联的 Generation对象的 broken 标志被设置为 true 时 |
int dowait(boolean timed, long nanos) |
|
3. Semaphore信号量
(1)介绍
- 也称 信号量
- 用来限制能同时访问共享资源的线程上限:即共享资源有多个,也允许多个线程访问,但需要限制数量
(2)用法
- 构造器定义 n 的信号量,可设置公平锁
//构造1:创建最大上限为n的信号量
Semaphore sem = new Semaphore( n )
//构造2:创建最大上限为n的信号量,且为公平
Semaphore sem = new Semaphore( n ,true)
- 调用 sem.acquire() 加锁, 调用 sem.release() 解锁
public static void main(String[] args) {
Semaphore sem = new Semaphore(3);
for(int i =0; i<10; i++){
new Thread(()->{
try{
sem.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
try{
sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
sem.release();
}
}).start();
}
}
(3)原理
- 和ReentrantLock类基本一样,定义了Sync静态内部类(NonfairSync 和 FairSync)
- 不同的是state为共享模式
(4)应用
- 使用Semaphore限流,让请求线程阻塞,高峰过去后再释放许可。可以用它改进数据库连接