1、什么是控制并发流程
控制并发流程的工具类,作用是帮助我们程序员更容易让线程之间合作
让线程之间相互配合,来满足业务逻辑
比如让线程A等待线程B执行完毕后在执行等策略
2、CountDownLatch倒计时门闩
例子:购物拼团,人满发车
原理:倒数结束之前,一直处于等待状态,直到倒计时结束了,此线程才继续工作
2.1、常用方法介绍
- new CountDownLatch(5);设置有几个数
- latch.countDown();谁减一谁调用countDown
- latch.await();谁等待谁调用await
2.2、一等多
一个线程等待多个线程执行完毕,在继续自己的工作
/** * @Classname CountLatchDemo1 * @Description 工厂中,质检,5个人检查,所有人都认为通过,才通过 * @Date 2021/5/18 22:04 * @Created by WangXiong */ public class CountDownLatchDemo1 { public static void main(String[] args) { final CountDownLatch latch = new CountDownLatch(5); ExecutorService service = Executors.newFixedThreadPool(5); for (int i = 0; i < 5; i++) { final int no = i + 1; Runnable runnable = new Runnable() { public void run() { try { Thread.sleep((long) (Math.random() * 10000)); System.out.println("No." + no + "完成了检查"); } catch (InterruptedException e) { e.printStackTrace(); } finally { latch.countDown(); } } }; service.submit(runnable); } System.out.println("等待5个工人检查完毕...."); try { latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("所有人都完成了工作,进入下一个环节"); service.shutdown(); } }
测试
2.3、多等一
多个线程等待一个线程,在同一出发
/** * @Classname CountDownLatchDemo2 * @Description 模拟100米跑步,5名选手都准备好了,只等裁判一声令下,所有人同时跑步 * @Date 2021/5/18 22:27 * @Created by WangXiong */ public class CountDownLatchDemo2 { public static void main(String[] args) { final CountDownLatch begin = new CountDownLatch(1); ExecutorService service = Executors.newFixedThreadPool(5); for (int i = 0; i < 5; i++) { final int no = i+1; Runnable runnable = new Runnable() { public void run() { System.out.println("No." + no + "准备完毕,等待发令枪"); try { begin.await(); System.out.println("No." + no + "开始跑步了"); } catch (InterruptedException e) { e.printStackTrace(); } } }; service.submit(runnable); } try { //模拟裁判员检查发令枪 Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("发令枪响,比赛开始!"); begin.countDown(); service.shutdown(); } }
测试
2.4、升级用法
五个远动员等待发令枪响,裁判等待五个运动员跑步结束
/** * @Classname CountDownLatchDemo2 * @Description 模拟长跑,我们需要模拟发令枪,以及到达终点的结束比赛 * @Date 2021/5/18 22:27 * @Created by WangXiong */ public class CountDownLatchDemo1AndDemo2 { public static void main(String[] args) { final CountDownLatch begin = new CountDownLatch(1); final CountDownLatch end = new CountDownLatch(5); ExecutorService service = Executors.newFixedThreadPool(5); for (int i = 0; i < 5; i++) { final int no = i+1; Runnable runnable = new Runnable() { public void run() { System.out.println("No." + no + "准备完毕,等待发令枪"); try { begin.await(); System.out.println("No." + no + "开始跑步了"); Thread.sleep((long) (Math.random() * 10000)); System.out.println("No." + no + "跑到终点了"); end.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } } }; service.submit(runnable); } try { //模拟裁判员检查发令枪 Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("发令枪响,比赛开始!"); begin.countDown(); try { end.await(); System.out.println("所有人到达终点,比赛结束"); } catch (InterruptedException e) { e.printStackTrace(); } service.shutdown(); } }
测试
2.5、注意点
CountDownLatch是不能够重用的
3、Semaphore信号量
用来限制有限资源的使用
信号量,可以类比为生活中的许可证
使用流程:
- 初始化Semaphore并指定许可证的数量
- 在执行方法前需要选获取许可证
- 执行完方法后需要释放许可证
常用方法
- new Semaphore(int permits, boolean fair):这里可以设置是否需要公平策略
- tryAcquire()判断是否可以拿到许可证
- acquire()获取许可证,可以响应中断
- acquireUninterruptibly()获取许可证,不响应中断
- releas()释放许可证
3.1、代码演示
/** * @Classname SemaphoreDemo1 * @Description 多个线程去等待许可证,没有获取到许可证的线程进行排查 * @Date 2021/5/18 22:56 * @Created by WangXiong */ public class SemaphoreDemo1 { static Semaphore semaphore = new Semaphore(3, true); public static void main(String[] args) { ExecutorService service = Executors.newFixedThreadPool(50); for (int i = 0; i < 100; i++) { service.submit(new Task()); } } static class Task implements Runnable{ public void run() { try { semaphore.acquire(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "获取到了许可证"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "释放了许可证"); semaphore.release(); } } }
测试
3.2、信号量特殊用法
信号量一次性可以获取一个或多个信号量,返回也可以返回一个或多个。获取和返回的许可证个数必须一致。
public class SemaphoreDemo1 { static Semaphore semaphore = new Semaphore(3, true); public static void main(String[] args) { ExecutorService service = Executors.newFixedThreadPool(50); for (int i = 0; i < 100; i++) { service.submit(new Task()); } } static class Task implements Runnable{ public void run() { try { semaphore.acquire(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "获取到了许可证"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "释放了许可证"); semaphore.release(3); } } }
测试
4、 Condition接口(条件对象)
4.1、Condition作用
线程1需要等待某个条件的时候,他就会执行Condition.await()方法,进入阻塞,线程2执行完后,会调用signal()方法, 唤醒线程1,继续执行
4.2、代码演示
/** * @Classname ConditionDemo1 * @Description 演示condition基本用法 * @Date 2021/5/18 23:11 * @Created by WangXiong */ public class ConditionDemo1 { private ReentrantLock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); void method1(){ lock.lock(); try{ System.out.println("条件不满足,开始await"); condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } void method2(){ lock.lock(); try { System.out.println("准备工作完成,唤醒其他的线程"); condition.signal(); }finally { lock.unlock(); } } public static void main(String[] args) { final ConditionDemo1 demo1 = new ConditionDemo1(); //自己沉睡不能自己唤醒自己 // demo1.method2(); new Thread(new Runnable() { public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } demo1.method2(); } }).start(); demo1.method1(); } }
测试
4.3、condition实现生产者消费者模式
/** * @Classname ConditionDemo2 * @Description 演示用Condition实现生产者和消费者模式 * @Date 2021/5/19 21:53 * @Created by WangXiong */ public class ConditionDemo2 { private int queueSize = 10; private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueSize); private Lock lock = new ReentrantLock(); private Condition noFull = lock.newCondition(); private Condition noEmpty = lock.newCondition(); class Consumer extends Thread{ @Override public void run() { while (true){ lock.lock(); try{ while (queue.size() == 0){ System.out.println("队列空,等待传递数据"); try { noEmpty.await(); } catch (InterruptedException e) { e.printStackTrace(); } } queue.poll(); noFull.signalAll(); System.out.println("从队列中取走了一个数据,队列剩余"+queue.size() + "元素"); }finally { lock.unlock(); } } } } class Produce extends Thread{ @Override public void run() { while (true){ lock.lock(); try{ while (queue.size() == queueSize){ System.out.println("队列满,等待有空余"); try { noFull.await(); } catch (InterruptedException e) { e.printStackTrace(); } } queue.offer(1); noEmpty.signalAll(); System.out.println("向队列插入了一个数据,队列剩余" + (queueSize - queue.size()) + "元素"); }finally { lock.unlock(); } } } } public static void main(String[] args) { ConditionDemo2 conditionDemo2 = new ConditionDemo2(); Produce produce = conditionDemo2.new Produce(); Consumer consumer = conditionDemo2.new Consumer(); produce.start(); consumer.start(); } }
测试
5、CyckuBarrier循环栅栏
和CountDownLatch很类似,都能阻塞一组线程
/** * @Classname CycliBarrierDemo * @Description 演示CyclicBarrier * @Date 2021/5/19 22:07 * @Created by WangXiong */ public class CycliBarrierDemo { public static void main(String[] args) { CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Runnable() { public void run() { System.out.println("所有人都到场了,大家同意出发!"); } }); for (int i = 0; i < 10; i++) { new Thread(new Task(i, cyclicBarrier)).start(); } } static class Task implements Runnable{ private int id; private CyclicBarrier cyclicBarrier; public Task(int id, CyclicBarrier cyclicBarrier) { this.id = id; this.cyclicBarrier = cyclicBarrier; } public void run() { System.out.println("线程"+id+"前往集合地点"); try { Thread.sleep((long) (Math.random() * 10000)); System.out.println("线程"+id+"到了集合地点,开始等待其他人到达"); cyclicBarrier.await(); System.out.println("线程"+id+"出发啦"); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } } }
测试
CyclicBarrier和CountDownLatch的区别
CyclicBarrier针对的是线程,CountDownLatch针对的是事件
CyclicBarrier可以重复使用,CountDownLatch不能重复使用