CountDownLatch
让一些线程阻塞,知道另一些线程完成一系列操作后才被唤醒。CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,调用线程会被阻塞。其他线程调用countDown方法会将计数器减一,当计数器变为零时,因调用await方法被阻塞的线程会被唤醒,继续执行。
先设定一个场景,公司到了六点,大家都要下班了,大Boss要等到大家都走了,才会锁门
代码
public class CountDownLatchTest {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
// 模拟员工装包
try {
TimeUnit.MILLISECONDS.sleep(new Random(1).nextInt());
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "\t 下班回家");
}, "employee"+i).start();
}
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t 锁门");
}, "boss").start();
}
}
大家可以多次尝试,会出现类似如下情况
employee2 下班回家
employee6 下班回家
employee5 下班回家
employee7 下班回家
employee0 下班回家
employee3 下班回家
boss 锁门
employee9 下班回家
employee4 下班回家
employee8 下班回家
employee1 下班回家
好家伙,因为有几个员工装包太慢了,黑心老板把几个员工锁在了公司,那这样怎么办呢?
真实情况下,老板知道公司一共有十名员工,可以让老板在门口等着,走一个人就查个数,知道走光了,再锁门
那这个时候就可以请出我们的CountDownLatch类了
public class CountDownLatchTest {
public static void main(String[] args) {
// 一共10名员工
CountDownLatch countDownLatch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
new Thread(() -> {
// 模拟员工装包
try {
TimeUnit.MILLISECONDS.sleep(new Random(3).nextInt());
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "\t 下班回家");
// 每走一个,就减一
countDownLatch.countDown();
}, "employee"+i).start();
}
new Thread(() -> {
// 让老板在门口等着,直到走完才锁门
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "\t 锁门");
}, "boss").start();
}
}
CyclicBarrier
CyclicBarrier的字面意思是可循环(cyclic)使用的屏障(barrier)。它要做的事情是,让一组线程到达一个屏障(也叫同步点)时被阻塞,知道最后一个线程到达屏障时,屏障才会打开,所有被屏障拦截的线程才会继续干活,线程进入屏障通过CyclicBarrier的await()方法
其实呢,他跟CountDownLatch相反,CountDownLatch是倒数,CyclicBarrier是正数,还是用一个小情景带大家看一下
情景:现在悟空在收集龙珠,找到一个龙珠没有什么用,必须要集齐七颗才能召唤神龙,那么先找到的龙珠,就让它等一会
public class CyclicBarrierTest {
public static void main(String[] args) {
// 构造参数:
// 1、查几个数,也就是要收集几颗龙珠
// 2、达到条件后要干嘛
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> System.out.println("召唤神龙"));
for (int i = 1; i <= 7; i++) {
new Thread(() -> {
// 模拟找龙珠的过程
try {
TimeUnit.MILLISECONDS.sleep(new Random(5).nextInt());
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "已经找到了");
// 找到了没有用,所以要等着
try {
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}, i + "星球").start();
}
}
}
Semaphore
信号量主要用于两个目的,一个是用于多个共享资源的互斥使用,另一个用于并发线程数的控制。
还是来个情景模拟
类似汽车抢车位,那么车位就是资源,每当有一辆车占用了车位(线程抢占资源),那么信号量就被减一,当没有车位可用时,其他车等待,但是当其他车驶出停车位,那么其他车就又能去抢这个新空出来的车位了
public class SemaphoreTest {
public static void main(String[] args) {
// 模拟三个车位
Semaphore semaphore = new Semaphore(3);
// 模拟6辆车
for (int i = 0; i < 6; i++) {
new Thread(() -> {
try {
// 锁定资源,相当于抢到了一个车位
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "\t抢到车位");
TimeUnit.SECONDS.sleep(new Random(3).nextInt());
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 释放资源,相当于车开走
semaphore.release();
System.out.println(Thread.currentThread().getName() + "\t开走了");
}
}, "t"+i).start();
}
}
}
输出:
t1 抢到车位
t0 抢到车位
t2 抢到车位
t1 开走了
t0 开走了
t2 开走了
t4 抢到车位
t4 开走了
t3 抢到车位
t5 抢到车位
t3 开走了
t5 开走了
内容均来源于学习资料,在学习过程中进行记录,如有侵权联系作者进行删除