CountDownLatch/CyclicBarrier/Semaphore
CountDownLatch
闭锁,在完成某些运算是,只有其他所有线程的运算全部完成,当前运算才继续执行
让一些线程阻塞,直到另一些线程完成一系列操作后才被唤醒
CountDownLatch主要有两种方法,当一个或多个线程调用await方法时,调用县城会被阻塞。
其他线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞),
当计数器的值变为零时,因调用await方法被阻塞的线程会被唤醒,继续执行
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
final CountDownLatch latch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
new Thread(()->{
System.out.println("第" + Thread.currentThread().getName() +"个");
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown();//实现原理是 if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) continue; 自旋锁
}, String.valueOf(i)).start();
}
//latch.await();
latch.await(10l, TimeUnit.SECONDS); //await的实质是在获取同步状态,同步状态state == 0成立,当前等待完成的点均已完成,主线程继续往下执行,否则,主线程进入等待队列自旋等待直到同步状态释放后state == 0。有些时候主线程是不能一直自旋等待,这个时候带超时时间的await就派上用场了,设置超时时间,如果在指定时间内N个点都未完成,返回false,主线程不再等待,继续往下执行
System.out.println("执行完毕");
}
}
CyclicBarrier
CyclicBarrier的字面意思是可循环(Cyclic)使用的屏障(Barrier).它要做的事情是让一组线程达到一个
屏障(也可以叫同步点)时被阻塞,知道最有一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活,
线程进入屏障通过CyclicBarrier的await()方法
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(7,()->{
System.out.println("7颗龙珠收集成功,召唤神龙");
});
for (int i = 0; i < 7; i++) {
new Thread(()->{
try {
System.out.println("第"+Thread.currentThread().getName() + "颗龙珠收集完成");
barrier.await();//CyclicBarrier同样提供带超时时间的await和不带超时时间的await:
//barrier.await(100l, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
},String.valueOf(i)).start();
}
}
}
//整个barrier.await()方法的核心是dowait方法的调用,我们来看看dowait的具体实现
//实现过程
//使用ReentrantLock保证每一次操作线程安全;
//线程等待/唤醒使用Lock配合Condition来实现;
//线程被唤醒的条件:等待超时或者所有线程都到达barrier。
private int dowait(boolean timed, long nanos)
throws InterruptedException, BrokenBarrierException,
TimeoutException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
final Generation g = generation;
if (g.broken)
throw new BrokenBarrierException();
if (Thread.interrupted()) {
breakBarrier();
throw new InterruptedException();
}
int index = --count;
if (index == 0) { // tripped
boolean ranAction = false;
try {
final Runnable command = barrierCommand;
if (command != null)
command.run();
ranAction = true;
nextGeneration();
return 0;
} finally {
if (!ranAction)
breakBarrier(); //唤醒所有等待中的线程
}
}
//上面这些代码 主要完成了当所有线程都到达同步点(barrier)时,唤醒所有的等待线程,一起往下继续运行,可根据参数barrierAction决定优先执行的线程。
// loop until tripped, broken, interrupted, or timed out
//该代码块为类似自旋锁,实现了线程未到达同步点(barrier)时,线程进入Condition自旋等待,直到等待超时或者所有线程都到达barrier时被唤醒
for (;;) {
try {
if (!timed)
trip.await();//trip = lock.newCondition(),当被唤醒时会break跳出循环
else if (nanos > 0L)
nanos = trip.awaitNanos(nanos);
} catch (InterruptedException ie) {
if (g == generation && ! g.broken) {
breakBarrier();
throw ie;
} else {
// We're about to finish waiting even if we had not
// been interrupted, so this interrupt is deemed to
// "belong" to subsequent execution.
Thread.currentThread().interrupt();
}
}
if (g.broken)
throw new BrokenBarrierException();
if (g != generation)
return index;
if (timed && nanos <= 0L) {
breakBarrier();
throw new TimeoutException();
}
}
} finally {
lock.unlock();
}
}
CountDownLatch 和 CyclicBarrier 的区别
- CountDownLatch:一个或者多个线程,等待其他多个线程完成某件事情之后才能执行;
- CyclicBarrier:多个线程互相等待,直到到达同一个同步点,再继续一起执行。
对于CountDownLatch来说,重点是“一个线程(多个线程)等待”,而其他的N个线程在完成“某件事情”之后,可以终止,也可以等待。而对于CyclicBarrier,重点是多个线程,在任意一个线程没有完成,所有的线程都必须等待。
CountDownLatch是计数器,线程完成一个记录一个,只不过计数不是递增而是递减,而CyclicBarrier更像是一个阀门,需要所有线程都到达,阀门才能打开,然后继续执行
Semaphore
信号量主要用于两个目的,一个是用于共享资源的互斥使用,另一个用于并发控制数的控制。
public class SemaphoreDemo {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 6; i++) {
new Thread(()->{
try {
semaphore.acquire();
System.out.println( "第"+Thread.currentThread().getName()+"辆抢到车位");
TimeUnit.SECONDS.sleep(3);
System.out.println( "第"+Thread.currentThread().getName()+"辆车停车3秒后离开");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();
}
},String.valueOf(i)).start();
}
}
}