闭锁(CountDownLatch):一种同步方法,可以延迟线程的进度直到线程到达某个终点状态。通俗的讲就是,一个闭锁相当于一扇大门,在大门打开之前所有线程都被阻断,一旦大门打开所有线程都将通过,但是一旦大门打开,所有线程都通过了,那么这个闭锁的状态就失效了,门的状态也就不能变了,只能是打开状态。也就是说闭锁的状态是一次性的,它确保在闭锁打开之前所有特定的活动都需要在闭锁打开之后才能完成。
确保某个计算在其需要的所有资源都被初始化之后才继续执行。二元闭锁(包括两个状态)可以用来表示“资源R已经被初始化”,而所有需要R的操作都必须先在这个闭锁上等待。
确保某个服务在其依赖的所有其他服务都已经启动之后才启动。
等待直到某个操作的所有参与者都就绪在继续执行。(例如:多人游戏中需要所有玩家准备才能开始)
CountDownLatch是JDK 5+里面闭锁的一个实现,允许一个或者多个线程等待某个事件的发生。CountDownLatch有一个正数计数器,countDown方法对计数器做减操作,await方法等待计数器达到0。所有await的线程都会阻塞直到计数器为0或者等待线程中断或者超时。
有三个工人在为老板干活,这个老板有一个习惯,就是当三个工人把一天的活都干完了的时候,他就来检查所有工人所干的活。记住这个条件:三个工人先全部干完活,老板才检查。所以在这里用Java代码设计两个类,Worker代表工人,Boss代表老板,具体的代码实现如下:
工人:
package lock;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* @author:peng
* @date: 2018/7/26 21:02
*/
public class Work implements Runnable {
private CountDownLatch downLatch;
private String name;
public Work(CountDownLatch downLatch, String name) {
this.downLatch = downLatch;
this.name = name;
}
public void run() {
this.doWork();
try {
TimeUnit.SECONDS.sleep(new Random().nextInt(10));
} catch (InterruptedException ie) {
}
System.out.println(this.name + "活干完了!");
this.downLatch.countDown();
}
private void doWork() {
System.out.println(this.name + "正在干活!");
}
}
老板:
package lock;
import java.util.concurrent.CountDownLatch;
/**
* @author:peng
* @date: 2018/7/26 21:03
*/
public class Boss implements Runnable {
private CountDownLatch downLatch;
public Boss(CountDownLatch downLatch) {
this.downLatch = downLatch;
}
public void run() {
System.out.println("老板正在等所有的工人干完活......");
try {
this.downLatch.await();
} catch (InterruptedException e) {
}
System.out.println("工人活都干完了,老板开始检查了!");
}
}
测试代码:
package lock;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 闭锁测试用例
*
* @author:peng
* @date: 2018/7/26 21:03
*/
public class CountDownLatchTest {
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
CountDownLatch latch = new CountDownLatch(3);
Work w1 = new Work(latch,"张三");
Work w2 = new Work(latch,"李四");
Work w3 = new Work(latch,"王二");
Boss boss = new Boss(latch);
executor.execute(w3);
executor.execute(w2);
executor.execute(w1);
executor.execute(boss);
executor.shutdown();
}
}
执行结果:
李四正在干活!
老板正在等所有的工人干完活......
王二正在干活!
张三正在干活!
李四活干完了!
王二活干完了!
张三活干完了!
工人活都干完了,老板开始检查了!
栅栏类似于闭锁,它能阻塞一组线程直到某个事件发生。 栅栏与闭锁的关键区别在于,所有的线程必须同时到达栅栏位置,才能继续执行。闭锁用于等待事件,而栅栏用于等待其他线程。
场景: 应用一些协议,比如几个家庭成员决定在某个地方集合,所有人在6:00在某地集合,到了以后要等待其他人,之后才能讨论去哪里吃饭。 并行迭代,将一个问题分成很多子问题,当一系列的子问题都解决之后(所有子问题线程都已经await()),此时将栅栏打开,所有子问题线程被释放,而栅栏位置可以留着下次使用。
接着上面的例子,还是这三个工人,不过这一次,这三个工人自由了,老板不用检查他们任务了,他们三个合作建桥,有三个桩,每人打一个,同时打完之后才能一起搭桥(搭桥需要三人一起合作)。也就是说三个人都打完桩之后才能继续工作。
package lock;
import java.util.concurrent.CyclicBarrier;
/**
* @author:peng
* @date: 2018/7/26 21:09
*/
public class CycWork implements Runnable {
private CyclicBarrier cyclicBarrier;
private String name;
public CycWork(CyclicBarrier cyclicBarrier, String name) {
this.name = name;
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println(name + "正在打桩,毕竟不轻松。。。。。");
try {
System.out.println(name + "不容易,终于把桩打完了。。。。");
cyclicBarrier.await();
Thread.sleep(5000);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
System.out.println(name + ":其他逗b把桩都打完了,又得忙活了。。。");
}
}
测试程序
package lock;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 栅栏测试用例
*
* @author:peng
* @date: 2018/7/26 21:09
*/
public class CyclicBarrierTest {
public static void main(String[] args) {
ExecutorService executorpool = Executors.newFixedThreadPool(3);
CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
CycWork work1 = new CycWork(cyclicBarrier, "张三");
CycWork work2 = new CycWork(cyclicBarrier, "李四");
CycWork work3 = new CycWork(cyclicBarrier, "王五");
executorpool.execute(work1);
executorpool.execute(work2);
executorpool.execute(work3);
executorpool.shutdown();
}
}
运行结果:
李四正在打桩,毕竟不轻松。。。。。
张三正在打桩,毕竟不轻松。。。。。
王五正在打桩,毕竟不轻松。。。。。
李四不容易,终于把桩打完了。。。。
张三不容易,终于把桩打完了。。。。
王五不容易,终于把桩打完了。。。。
王五:其他逗b把桩都打完了,又得忙活了。。。
李四:其他逗b把桩都打完了,又得忙活了。。。
张三:其他逗b把桩都打完了,又得忙活了。。。
package lock;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
/**
* @author:peng
* @date: 2018/7/26 21:21
*/
public class SemaphoreTest {
private Semaphore smp = new Semaphore(3);
private Random rnd = new Random();
class TaskDemo implements Runnable {
private String id;
TaskDemo(String id) {
this.id = id;
}
@Override
public void run() {
try {
smp.acquire();
System.out.println("Thread " + id + " is working");
Thread.sleep(rnd.nextInt(3000));
smp.release();
System.out.println("Thread " + id + " is over");
} catch (InterruptedException e) {
}
}
}
public static void main(String[] args) {
SemaphoreTest semaphoreDemo = new SemaphoreTest();
//注意我创建的线程池类型,
ExecutorService se = Executors.newCachedThreadPool();
se.submit(semaphoreDemo.new TaskDemo("a"));
se.submit(semaphoreDemo.new TaskDemo("b"));
se.submit(semaphoreDemo.new TaskDemo("c"));
se.submit(semaphoreDemo.new TaskDemo("d"));
se.submit(semaphoreDemo.new TaskDemo("e"));
se.submit(semaphoreDemo.new TaskDemo("f"));
se.shutdown();
}
}
https://blog.csdn.net/yujin753/article/details/46125283
http://peaceland.cn/2016/03/28/cyclicbarrier_semaphore/
https://www.cnblogs.com/NewMan13/p/7792365.html
CountDownLatch、CyclicBarrier、Semaphore的区别