JUC封装了许多并发工具类方便的去对并发线程间的同步访问控制,减少代码的重复开发.
伪代码案例
将计算年收入这个大任务分成3个小任务并发执行.3个小任务分别是计算1-4月收入, 计算5-9月收入,计算10-12月收入等到3个小任务都执行完后, 才去根据3个计算任务的结果汇总年收入 CountDownLatch latch = new CountDownLatch(3); //计数器值为3 // 给每个任务创建一个线程并发执行 //线程1执行操作A { do something .. 计算1-4月收入 latch.countDown(); //计数器值减 1 } //线程2执行操作B { do something ...计算5-9月收入 latch.countDown(); //计数器值减 1 } //线程3执行操作C { do something ...计算10-12月收入 latch.countDown(); //计数器值减 1 } //阻塞当前线程,直到latch的值为0才继续向下执行 latch.await(); //对年收入进行汇总 计算年收入 = 1-4月收入 + 5-9月收入 + 10-12月收入 ....循环屏障, 使用起来有点像可循环的循环计数器(计算线程等待的数量)
给循环屏障,CyclicBarrier设置一个等待线程的数量A(屏障点) ,然后各个线程互相去等待, 直到等待的线程的数量达到A(屏障点)时这些所有等待的线程才会一起继续往下执行.如下图
CyclicBarrier内部通过一个boolean变量维护CyclicBarrier是否破损(“坏掉”), 当屏障出现破损时所有功能失效,并清空唤醒在此屏障锁上等待的所有线程
屏障破损情况:
CyclicBarrier初始化时,broken=false,表示屏障未破损。如果正在等待的线程被中断,则broken=true,表示屏障破损。如果正在等待的线程超时,则broken=true,表示屏障破损。如果有线程调用CyclicBarrier.reset()方法,则broken=false,表示屏障回到未破损状态。伪代码案例
有100个人参加活动,每个人等待组成队伍, 每3个人可以组成一队. 就可以开始参与活动. 然后不断循环. //创建CyclicBarrier 屏障 ,每当等待线程的数量达到3后就会执行这个线程回调方法 CyclicBarrier barrier = new CyclicBarrier(3, new Runnable() { public void run() { System.out.println("3名人员组队成功, 开始参与活动"); } }); //创建10个线程并发去抢占活动名额 for (int i = 0; i < 100; i++) { new Thread(()->{ Thread.sleep(3000); System.out.println(Thread.currentThread().getName() + "-到达,等待组队完成"); // 当Thread-3到来时,由于是vip之前等待的线程不算要重新开始 if(Thread.currentThread().getName().equals("Thread-3")) { //将屏障设置为初始状态,清空在屏障锁等待的线程.这样barrier就可以重复使用 barrier.reset(); } try { // 线程等待, CyclicBarrier还需达到屏障点的等待线程数-1 //(当屏障状态破损,此方法不生效)当前线程进入此屏障锁的等待队列 //当等待超过1分钟都没到达屏障点时置屏障为破损状态 barrier.await(1, TimeUnit.SECONDS); } catch (Exception e) { } // 参加完活动后,程序继续往下执行 3人组队参与活动结束各回各家各找爸妈.... }).start(); } // 每隔0.5秒统计等待组成人员个数 ----监控等待线程数 new Thread(()->{ while(true) { Thread.sleep(500); //获取在循环屏障的等待线程个数 System.out.println("等待的线程数 " + barrier.getNumberWaiting()); //屏障是否破损,为true就是破损,需reset重置才能循环使用 System.out.println("is broken " + barrier.isBroken()); } }).start();CyclicBarrier 与 CountDownLatch 区别
CountDownLatch 是一次性的,CyclicBarrier提供reset功能 是可以可循环利用的CountDownLatch是一个线程B等到其他N个线程都完成了,线程B才继续往下执行.而CyclicBarrier是N个线程都到达同一个水平线才一起开始起跑伪代码:
//交换String类型数据 Exchanger<String> exch = new Exchanger<>(); //线程A { String res1 = exch.exchange(“data”); //在交换前会阻塞在此 } //线程B里 { String res2 = exch.exchange(“bbq”); //在交换前会阻塞在此 } //两个线程同时执行,当两个线程都存储了数据后,会把数据交换并返回 这时res1结果为bbq, res2结果为data和 wait/notify机制区别
wait和notify都是Object中的方法, wait和notify方法要在同步块synchronized中才能调用. 并且在调用这两个方法前必须先获得锁对象.而park不需要获取某个对象的锁就可以锁住线程。notify只能随机选择一个线程唤醒,无法唤醒指定的线程,unpark却可以唤醒一个指定的线程。