1.CountDownLatch
A synchronization aid that allows one or more threads to wait until a set of
operations being performed in other threads completes.
CountDownLatch
是一个辅助线程同步的工具,允许一个或多个线程等待其他线程执行完成。但是只能使用一次,不能重置。
1.1 核心方法
构造函数CountDownLatch(int count)
the number of times {@link #countDown} must be invoked before threads can pass through {@link #await}
传入的count
数,通俗的讲就是需要主线程等待其他线程执行完成的线程个数,即主线程需要等待多少个线程执行完成,实际count
充当的是一个计数器的作用。
await()
见名知意,该方法会阻塞当前线程,直到count
清零为止。
countDown()
计数器 count
减一,当 count
为零时,解除所有阻塞的线程。
await(long timeout, TimeUnit unit)
该方法多一个超时时间参数,过了超时时间不管计数器count
有没有清零,所有阻塞线程都会解除阻塞继续往下执行。可以防止线程由于意外导致中断没有调用到countDown()
方法,导致阻塞线程一直处于阻塞等待状态
1.2 示例
比如一个大人照顾三个孩子吃饭,大人需要照顾三个孩子吃完饭了之后才能开始吃饭。
public static void main(String[] args) throws InterruptedException {
int count = 3;
CountDownLatch latch = new CountDownLatch(count);
System.out.println("==> 饭来了~");
for (int i = 0; i < count; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + "==> 吃完饭了");
}finally {
//子线程执行完成,清除计数器
latch.countDown();
}
},"孩子"+ i).start();
}
//主线程等待其他线程执行完成
latch.await();
System.out.println("==> 大人开始吃饭");
}
执行结果
==> 饭来了~
孩子0 ==> 吃完饭了
孩子2 ==> 吃完饭了
孩子1 ==> 吃完饭了
==> 大人开始吃饭
2、CyclicBarrier
一组线程之间相互等待,直到所有线程都达到阻塞点await
之后,才往下执行。
2.1 核心方法
构造函数1 – CyclicBarrier(int parties)
parties
相互之间等待的线程数
构造函数2 – CyclicBarrier(int parties, Runnable barrierAction)
barrierAction
所有线程达到阻塞点,解除阻塞时的回调
await()
阻塞点,所有线程都达到该阻塞点之后,才能继续往下执行。
await(long timeout, TimeUnit unit)
带有超时时间的阻塞点。如果超过时间过了,但是还有线程未达到阻塞点则会抛出TimeoutException
异常,其他等待线程则抛出BrokenBarrierException
异常
2.2 示例
一个大人和3个小孩一起吃饭,只有他们都吃完了之后才能吃水果
public static void main(String[] args) throws BrokenBarrierException, InterruptedException {
int count = 3;
final CyclicBarrier barrier = new CyclicBarrier(4, () -> {
System.out.println("都吃完饭了,开始发水果 ~ ");
});
System.out.println("开饭咯~");
for (int i = 0; i < count; i++) {
final int time = i;
new Thread(() -> {
String childName = Thread.currentThread().getName();
try {
System.out.println(childName + " ==> 开始吃饭");
TimeUnit.SECONDS.sleep(time);//吃饭耗时
System.out.println(childName + " ==> 吃完饭了");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//子线程执行完成,调用清除计数器
try {
barrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
System.out.println(childName + " ==> 开始吃水果");
}, "孩子" + i).start();
}
System.out.println("大人 ==> 开始吃饭");
TimeUnit.SECONDS.sleep(0);//吃饭耗时
System.out.println("大人 ==> 吃完饭了");
barrier.await();
System.out.println("大人 ==> 开始吃水果");
}
执行结果
开饭咯~
大人 ==> 开始吃饭
孩子0 ==> 开始吃饭
孩子2 ==> 开始吃饭
孩子1 ==> 开始吃饭
孩子0 ==> 吃完饭了
大人 ==> 吃完饭了
孩子1 ==> 吃完饭了
孩子2 ==> 吃完饭了
都吃完饭了,开始发水果 ~
孩子2 ==> 开始吃水果
孩子0 ==> 开始吃水果
孩子1 ==> 开始吃水果
大人 ==> 开始吃水果
3、Semaphore
对一组线程,允许同时执行的线程数,类似限流器的作用,只有获取到Semaphore
的许可后才能执行。
3.1 核心方法
构造函数 Semaphore(int permits)
permits
许可证的数量,即允许同时执行的线程数量。
acquire()
获取许可证。如果Semaphore
中的许可证已用完,则当前线程会一直处于阻塞状态,直到有可有的许可证出现为止。
release()
释放(交还)许可证。
3.3 示例
5个孩子游玩游戏屋,游戏屋同一时间只能供2人游玩。只有从老师手里拿到了凭证才能进去游玩。
public static void main(String[] args) {
List<Thread> children = new ArrayList<>();
Semaphore semaphore = new Semaphore(2);
for (int i = 0; i < 5; i++) {
children.add(new Thread(() -> {
String name = Thread.currentThread().getName();
try {
// 找老师获取凭证
semaphore.acquire();
System.out.println(String.format("%s ==> 拿到凭证,进去游玩", name));
TimeUnit.SECONDS.sleep(1);
System.out.println(String.format("%s ==> 出门,交还凭证 !", name));
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "child-" + i));
}
children.forEach(Thread::start);
}
执行结果
child-1 ==> 拿到凭证,进去游玩
child-0 ==> 拿到凭证,进去游玩
child-0 ==> 出门,交还凭证 !
child-1 ==> 出门,交还凭证 !
child-2 ==> 拿到凭证,进去游玩
child-3 ==> 拿到凭证,进去游玩
child-3 ==> 出门,交还凭证 !
child-2 ==> 出门,交还凭证 !
child-4 ==> 拿到凭证,进去游玩
child-4 ==> 出门,交还凭证 !