一、CountDownLatch
CountDownLatch类位于java.util.concurrent包下,利用它可以实现类似计数器的功能。CountDownLatch用来控制一个线程等待多个线程。内部维护了一个cnt,每次调用countDown()方法就会让该计数器的值减1。当计数器的值减少到0的时候,就会唤醒那些因为调用了await()方法而等待的线程。
CountDownLatch类只提供了一个构造器:
public CountDownLatch(int count) { }; //参数count为计数值
CountDownLatch类中三个最重要的方法:
public void await() throws InterruptedException { }; //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { }; //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
public void countDown() { }; //将count值减1
使用示例:
import java.util.concurrent.CountDownLatch;
public class CountdownLatchExample {
public static void main(String[] args){
final CountDownLatch latch = new CountDownLatch(2);
// 初始化计数值为 2,没调用一次countDown()方法,计数值减1
// 计数值减为0时,唤醒那些条用await()方法而等待的线程
new Thread(){
public void run(){
try {
System.out.println("子线程" + Thread.currentThread().getName() + "正在执行");
Thread.sleep(300);
System.out.println("子线程" + Thread.currentThread().getName() + "执行完毕");
latch.countDown();
} catch (InterruptedException e){
e.printStackTrace();
}
};
}.start();
new Thread(){
public void run(){
try {
System.out.println("子线程" + Thread.currentThread().getName() + "正在执行");
Thread.sleep(300);
System.out.println("子线程" + Thread.currentThread().getName() + "执行完毕");
latch.countDown();
} catch (InterruptedException e){
e.printStackTrace();
}
};
}.start();
try {
System.out.println("等待两个子线程执行完毕");
latch.await();
// 条用await()将主线程挂起,当countDown()方法使得计数值减为0时,
// 开始唤醒主线程
System.out.println("主线程开始执行");
Thread.sleep(1000);
} catch (InterruptedException e){
e.printStackTrace();
} finally {
System.out.println("主线程执行完毕");
}
}
}
执行结果:
子线程Thread-0正在执行
等待两个子线程执行完毕
子线程Thread-1正在执行
子线程Thread-0执行完毕
子线程Thread-1执行完毕
主线程开始执行
主线程执行完毕
主线程调用await()方法进入等待,一定要等待其他线程调用countDown()方法,使得计数器的值减到0才开始继续执行。
二、CyclicBarrier
用来控制多个线程互相等待,只有当多个线程都到达时,这些线程才会继续执行。
和 CountdownLatch 相似,都是通过维护计数器来实现的。线程执行 await() 方法之后计数器会减 1,并进行等待,直到计数器为 0,所有调用 await() 方法而在等待的线程才能继续执行。
CyclicBarrier 和 CountdownLatch 的一个区别是,CyclicBarrier 的计数器通过调用 reset() 方法可以循环使用,所以它才叫做循环屏障。
CyclicBarrier 有两个构造函数,其中 parties 指示计数器的初始值,barrierAction 在所有线程都到达屏障的时候会执行一次。
CyclicBarrier的两个构造函数:
public CyclicBarrier(int parties) {
this(parties, null);
}
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this.parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
CyclicBarrier使用示例:
public class CyclicBarrierExample {
public static void main(String[] args){
final int totalThread = 10;
CyclicBarrier cyclicBarrier = new CyclicBarrier(totalThread);
ExecutorService executorService = Executors.newCachedThreadPool();
for(int i = 0; i < totalThread; i++){
executorService.execute(() -> {
System.out.println("before");
try {
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e){
e.printStackTrace();
} finally {
System.out.println("after");
}
});
}
}
}
输出结果:
before
before
before
before
before
before
before
before
before
before
after
after
after
after
after
after
after
after
after
after
输出"before"之后调用await(),会使得计数器的值减1,计数器的初始值为10,所以,调用10次await()函数之后,计数器的值减少为0,所有因为await()而等待的线程将会继续执行。
三、Semaphore
semaphore翻译过来就是信号量,类似于操作系统中的信号量,可以控制互斥资源的访问线程数。
通过acquire()获取一个许可,如果没有,则会一直等待,直到获得许可。通过release()释放一个许可。
代码示例 ( 8个工人,5台机器 ):
public class SemaphoreExample {
public static void main(String[] args){
int N = 8; //工人数
Semaphore semaphore = new Semaphore(5); // 5台机器
for(int i = 0; i < N; i++){
new Worker(i,semaphore).start();
}
}
static class Worker extends Thread{
private int num;
private Semaphore semaphore;
public Worker(int num,Semaphore semaphore){
this.num = num;
this.semaphore = semaphore;
}
@Override
public void run() {
try {
semaphore.acquire();
System.out.println("工人-" + this.num + "占用机器");
Thread.sleep(1000);
System.out.println("工人-" + this.num + "释放机器");
semaphore.release();
} catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
输出结果:
工人-1占用机器
工人-0占用机器
工人-2占用机器
工人-3占用机器
工人-4占用机器
工人-2释放机器
工人-0释放机器
工人-3释放机器
工人-1释放机器
工人-6占用机器
工人-7占用机器
工人-5占用机器
工人-4释放机器
工人-5释放机器
工人-6释放机器
工人-7释放机器
参考博客: