CountDownLatch
-
即倒数栅栏,有两种典型用法:一等多、多等一
-
await()
:调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行 -
countDown()
:将count()值减1,直到为0时,等待的线程会被唤醒 -
CountDownLatch(int count)
:构造函数,参数count为需要倒数的数值 -
一等多:一个线程等待多个线程都执行完毕,再继续自己的工作
模拟工厂中,质检流程,5个质检工人检查,所有人都认为通过,才算通过
public class CountDownLatchDemo1 { public static void main(String[] args) throws InterruptedException { /** * 需要倒数5次 */ 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() { @Override public void run() { try { Thread.sleep((long) (Math.random()*10000)); System.out.println("No." + no + "完成了检查"); } catch (InterruptedException e) { e.printStackTrace(); } finally { /** * 每个线程检查完都要countDown一次 */ latch.countDown(); } } }; service.submit(runnable); } System.out.println("等待5个人检查完...."); /** * 每个线程都要在此等待,最后一个线程除外 */ latch.await(); System.out.println("所有人都完成了工作,进入下一个环节"); } }
-
多等一:多个线程等待某一个线程的信号同时开始执行
模拟100米跑步,5名选手都准备好了,只等裁判员一声令下,所有人同时开始跑步。
public class CountDownLatchDemo2 { public static void main(String[] args) throws InterruptedException { 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() { @Override public void run() { System.out.println("No." + no + "准备完毕,等待发令枪"); try { begin.await(); System.out.println("No." + no + "开始跑步了"); } catch (InterruptedException e) { e.printStackTrace(); } } }; service.submit(runnable); } //检查发令枪 Thread.sleep(5000); System.out.println("发令枪响,比赛开始!"); begin.countDown(); } }
Semaphore
-
Semaphore可以用来限制或管理数量有限的资源的使用情况
-
使用流程
- 初始化Semaphore并指定许可证
- 在需要许可证的地方使用acquire()方法或者acquireUninterruptibly()方法获取
- 在任务结束后,调用release()来释放许可证
-
new Semaphore(int permits, boolean fair)
:构造函数,permits表示许可证数量,fair表示是否使用公平策略,如果传入true,则将等待获取许可证的线程放进FIFO队列中,有了新的许可证便分发给等待时间最长的线程 -
acquire()
:获取许可证 -
release()
:归还许可证/**信号量的基本用法**/ public class SemaphoreDemo { 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()); } service.shutdown(); } static class Task implements Runnable{ @Override 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(); } } }
-
获取和释放的许可证数量必须一致,否则随着时间推移,最后许可证数量不够用,程序卡死
-
获取和释放许可证对线程并无要求,这个线程获取,可由其它线程释放
Condition
-
当线程1需要等待某个条件时,执行condition.await()方法,线程进入阻塞状态
-
假设线程2去执行对应的条件,直到条件达成,线程2去执行condition.signal()方法,唤醒线程1
-
signal()
:公平,只会唤醒等待时间最长的那个线程 -
signalAll()
:唤醒所有的正在等待的线程/** * 基本用法 */ public class ConditionDemo1 { private ReentrantLock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); void method1() throws InterruptedException { lock.lock(); try { System.out.println("条件不满足,开始await"); /** * 条件不满足,开始阻塞 */ condition.await(); System.out.println("条件满足了,开始执行后续的任务"); } finally { lock.unlock(); } } void method2() { lock.lock(); try { System.out.println("准备工作完成,唤醒其它线程"); condition.signal(); } finally { lock.unlock(); } } public static void main(String[] args) throws InterruptedException { ConditionDemo1 conditionDemo1 = new ConditionDemo1(); new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1000); conditionDemo1.method2(); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); conditionDemo1.method1(); } }
-
用条件对象实现生产者模式
public class ConditionDemo2 { private int queueSize = 10; private PriorityQueue<Integer> queue = new PriorityQueue<>(queueSize); private Lock lock = new ReentrantLock(); private Condition notFull = lock.newCondition(); private Condition notEmpty = lock.newCondition(); public static void main(String[] args) { ConditionDemo2 conditionDemo2 = new ConditionDemo2(); Producer producer = conditionDemo2.new Producer(); Consumer consumer = conditionDemo2.new Consumer(); producer.start(); consumer.start(); } class Consumer extends Thread{ @Override public void run() { consume(); } private void consume() { while (true) { lock.lock(); try { while (queue.size() == 0) { System.out.println("队列空,等待数据"); try { notEmpty.await(); } catch (InterruptedException e) { e.printStackTrace(); } } queue.poll(); notFull.signal(); System.out.println("从队列中取走了一个数据,队列剩余" + queue.size() + "个元素"); } finally { lock.unlock(); } } } } class Producer extends Thread{ @Override public void run() { produce(); } private void produce() { while (true) { lock.lock(); try { while (queue.size() == queueSize) { System.out.println("队列满,等待有空余"); try { notFull.await(); } catch (InterruptedException e) { e.printStackTrace(); } } queue.offer(1); notEmpty.signal(); System.out.println("向队列中插入了一个数据,队列剩余空间" + (queueSize - queue.size())); } finally { lock.unlock(); } } } } }
CyclicBarriar
-
CyclicBarrier循环栅栏和CountDownLatch很类似,都能阻塞一组线程
下面例子中演示有10个线程,5个一批5个一批地放行
public class CyclicBarrierDemo { public static void main(String[] args) { CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Runnable() { @Override 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; } @Override 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 | BrokenBarrierException e) { e.printStackTrace(); } } } }
-
CyclicBarrier和CountDownLatch的区别
- 作用不同:CyclicBarrier要等待固定数量的线程都到达了栅栏位置才能继续执行,而CountDownLatch只需等待数字到达0,也就是说,CountDownLatch用于事件,CyclicBarrier用于线程
- 可重用性不同:CountDownLatch在倒数到0并触发门闩打开后,就不能再次使用了,除非新建实例;而CyclicBarrier可以重复使用