本文从实际场景出发,对j.u.c
包下的四个并发工具的使用进行演示。相信看完以后会对他们的区别和关联有更多了解。
CyclicBarrier
j.u.c.CyclicBarrier
中文翻译循环栅栏,即可以在多个线程都执行完成后再触发,且支持循环使用。
场景一
下面场景是5个客人相约到饭店吃饭,只有都到了才能点餐。parties设置为5,会等待5个线程都执行到await()处才会进行下一步。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public class TestCyclicBarrier {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(5, () -> System.out.println("客人都已到达"));
for (int i = 0; i < 5; i++) { int finalI = i; Runnable runnable = () -> { try { TimeUnit.SECONDS.sleep(finalI + 1); System.out.println(Thread.currentThread().getName() + "到达"); cyclicBarrier.await(); System.out.println(Thread.currentThread().getName() + "点餐");
} catch (Exception e) { e.printStackTrace(); } }; Thread thread = new Thread(runnable); thread.setName("客人" + (i + 1)); thread.start(); } } }
|
结果
5个人都到了才能点餐
1 2 3 4 5 6 7 8 9 10 11
| 客人1到达 客人2到达 客人3到达 客人4到达 客人5到达 客人都已到达 客人5点餐 客人1点餐 客人2点餐 客人4点餐 客人3点餐
|

场景二
下面场景是拼单模式,只要有2个客人就能拼单成功,没有拼到就会一直等待。
parties设置为2,每当有2个线程执行到await()处就会进行下一步,第三轮由于只有一个线程,则会一直等待。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public class TestCyclicBarrier {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(2, () -> System.out.println("客人都已到达"));
for (int i = 0; i < 5; i++) { int finalI = i; Runnable runnable = () -> { try { TimeUnit.SECONDS.sleep(finalI + 1); System.out.println(Thread.currentThread().getName() + "到达"); cyclicBarrier.await(); System.out.println(Thread.currentThread().getName() + "点餐");
} catch (Exception e) { e.printStackTrace(); } }; Thread thread = new Thread(runnable); thread.setName("客人" + (i + 1)); thread.start(); } } }
|
结果
客人5只能看着
1 2 3 4 5 6 7 8 9 10 11
| 客人1到达 客人2到达 客人都已到达 客人2点餐 客人1点餐 客人3到达 客人4到达 客人都已到达 客人3点餐 客人4点餐 客人5到达
|

CountDownLatch
j.u.c.CountDownLatch
意为倒计时门闩,一般用作主线程等所有线程都执行完毕后,再执行自己的逻辑。或者等门闩开启,多个线程同时执行。
场景一
下面场景是等员工都下班了,保安负责给办公室关灯。
设定了count=5,每一个线程调用countDown()
,则count–。当count=0时,门闩打开,给主线程放行。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public class TestCountDownLatch { public static void main(String[] args) throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(5);
for (int i = 0; i < 5; i++) { int finalI = i; Runnable runnable = () -> { try { TimeUnit.SECONDS.sleep(finalI + 1); System.out.println(Thread.currentThread().getName() + "下班"); countDownLatch.countDown();
} catch (Exception e) { e.printStackTrace(); } }; Thread thread = new Thread(runnable); thread.setName("员工" + (i + 1)); thread.start(); }
System.out.println("下班时间到,保安在等待关灯。。。"); countDownLatch.await(); System.out.println("关灯");
} }
|
结果一
1 2 3 4 5 6 7
| 下班时间到,保安在等待关灯。。。 员工1下班 员工2下班 员工3下班 员工4下班 员工5下班 关灯
|

结果二
如果不设置门闩,运行结果是
1 2 3 4 5 6 7
| 下班时间到,保安在等待关灯。。。 关灯 员工1下班 员工2下班 员工3下班 员工4下班 员工5下班
|

这样就得摸黑下班了( ´•̥ו̥` )
场景二
下面场景是员工陆续下班后,等待班车发车,然后一起出发。
设定了count=1,当执行了countDown()
,所有阻塞在await()
的线程会同时启动
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public class TestCountDownLatch2 { public static void main(String[] args) throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(1);
for (int i = 0; i < 5; i++) { int finalI = i; Runnable runnable = () -> { try { TimeUnit.SECONDS.sleep(finalI + 1); System.out.println(Thread.currentThread().getName() + "下班,等车中"); countDownLatch.await(); System.out.println(Thread.currentThread().getName() + "出发");
} catch (Exception e) { e.printStackTrace(); } }; Thread thread = new Thread(runnable); thread.setName("员工" + (i + 1)); thread.start(); }
TimeUnit.SECONDS.sleep(6); System.out.println("到点了"); countDownLatch.countDown(); } }
|
结果
早下班也没用,班车没来
1 2 3 4 5 6 7 8 9 10 11
| 员工1下班,等车中 员工2下班,等车中 员工3下班,等车中 员工4下班,等车中 员工5下班,等车中 到点了 员工1出发 员工5出发 员工4出发 员工2出发 员工3出发
|

Semaphore
j.u.c.Semaphore
意为信号量,可以理解为资源许可证,只有拿到许可证的线程,才能执行对应的工作,在执行完后需要归还。
场景一
下面场景是公司的餐厅座位有限,员工只能排队用餐。
设置permits=2,表示只有2张许可证,拿不到许可的线程只能等待
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public static void main(String[] args) {
Semaphore semaphore = new Semaphore(2);
for (int i = 0; i < 5; i++) { Runnable runnable = () -> { try { semaphore.acquire(); System.out.println(Thread.currentThread().getName() + "到达餐厅开始吃饭"); TimeUnit.SECONDS.sleep(2); System.out.println(Thread.currentThread().getName() + "吃完了离开餐厅"); semaphore.release();
} catch (Exception e) { e.printStackTrace(); } }; Thread thread = new Thread(runnable); thread.setName("员工" + (i + 1)); thread.start(); } }
|
结果
到底什么公司这么坑啊!!
1 2 3 4 5 6 7 8 9 10
| 员工1到达餐厅开始吃饭 员工2到达餐厅开始吃饭 员工2吃完了离开餐厅 员工1吃完了离开餐厅 员工3到达餐厅开始吃饭 员工4到达餐厅开始吃饭 员工4吃完了离开餐厅 员工3吃完了离开餐厅 员工5到达餐厅开始吃饭 员工5吃完了离开餐厅
|

Exchanger
j.u.c.Exchanger
意为交换机,可以使两个线程在执行exchange()
时交换信息。
未完待续。。。
场景一
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| public class TestExchanger {
public static void main(String[] args) {
Exchanger<Object> exchanger = new Exchanger<>();
Runnable runnable = () -> { try { String field = "咖啡"; Object o = exchanger.exchange(field); System.out.println(Thread.currentThread().getName() + " 用 " + field + " 换了 " + o); } catch (InterruptedException e) { e.printStackTrace(); } }; Thread thread = new Thread(runnable); thread.setName("员工A"); thread.start();
Runnable runnable2 = () -> { try { String field = "奶茶"; Object o = exchanger.exchange(field); System.out.println(Thread.currentThread().getName() + " 用 " + field + " 换了 " + o); } catch (InterruptedException e) { e.printStackTrace(); } }; Thread thread2 = new Thread(runnable2); thread2.setName("员工B"); thread2.start(); } }
|
结果
1 2
| 员工B 用 奶茶 换了 咖啡 员工A 用 咖啡 换了 奶茶
|
