Java - CountDownLatch和CyclicBarrier小结

    技术2023-05-17  85

    背景

    这两JUC类适合放一起比较,随便写写,简单小结。

    CountDownLatch

    要点:

    底层采用AQS队列,源码很简单300行。

    使用:

    初始化对象并填入(计数器)值,例如:new CountDownLatch(3)。多线程执行完毕时,触发countdown方法将计数器值减1,当值为0时,主线程中被Latch拴住的代码开始执行。

    用法一:

    public class CountDownLatch_v1 { //计数器设为3 static CountDownLatch latch = new CountDownLatch(3); public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread( () -> { latch.countDown(); System.out.println("T1 is OK !"); } ); Thread t2 = new Thread( () -> { System.out.println("T2 is OK !"); latch.countDown(); } ); Thread t3 = new Thread( () -> { System.out.println("T3 is OK !"); latch.countDown(); } ); t1.start(); t2.start(); t3.start(); latch.await(); //main线程阻塞中,等待t1 t2 t3都countDown后,计数器从3变为0后继续运行 System.out.println("Every T is OK !"); } }

    用法二:

    public class CountDownLatch_v2 { static CountDownLatch latch = new CountDownLatch(1); public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread( () -> { try { latch.await(); System.out.println("T1 Beginning !"); } catch (InterruptedException e) { e.printStackTrace(); } } ); Thread t2 = new Thread( () -> { try { latch.await(); System.out.println("T2 Beginning !"); } catch (InterruptedException e) { e.printStackTrace(); } } ); Thread t3 = new Thread( () -> { try { latch.await(); System.out.println("T3 Beginning !"); } catch (InterruptedException e) { e.printStackTrace(); } } ); t1.start(); t2.start(); t3.start(); System.out.println("Start !"); latch.countDown(); } }

    用法场景:

    Main线程等待多个线程T执行完毕后才能执行。多个线程T执行各自的代码段,执行完在线程中执行CountDown方法(减1),减到0后触发Main线程执行。Main线程等待 T1,T2 两个线程 ,只有两个线程执行达到固定次数(线程内可反复CountDown)后Main线程才能执行。为达到最大并行度,Main线程在某一时刻,启动多个线程T。 PS:new CountDownLatch(1); 多个T线程中 执行await(); //所有T线程阻塞等待。 Main线程中执行CountDown //主线程一声令下,多个T线程开始执行。情况3和1的结合,多个线程T都干完后,同时开启多个线程M做下一件事。

    不足:

    CountDownLatch 对象New出来后只能使用一次。无法重新赋值反复使用。

    CyclicBarrier

    要点:

    源码不到500,方法不多,采用RE重入锁。多个线程中通过调用barrier.await相互等待,达到触发点后,触发新线程中自定义方法执行。精髓在于Cyclic,定义的触发方法可被反复多次触发(前提:CyclicBarrier没有Broken)。

    使用:

    CyclicBarrier构造方法中,指定触发阈值、触发方法(新线程)。达到触发次数时,开启新线程执行触发方法。子线程中调用barrier.await一次即可(写多次await调用会抛异常),然后就被绊倒了(被阻塞)。

    用法一:CyclicBarrier循环多次触发。

    public class CyclicBarrier_v2 { static CyclicBarrier barrier ; public static void main(String[] args) throws InterruptedException { barrier = new CyclicBarrier(3, () -> { System.out.println("Every one is OK !"); }); Thread t1 = new Thread( () -> { try { for(int i=0; i <10; i++) { System.out.println("T1 is OK !"); barrier.await(); } } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } ); Thread t2 = new Thread( () -> { try { for(int i=0; i <10; i++) { System.out.println("T2 is OK !"); barrier.await(); } } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } ); Thread t3 = new Thread( () -> { try { for(int i=0; i <10; i++) { System.out.println("T3 is OK !"); barrier.await(); } } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } ); t1.start(); t2.start(); t3.start(); } }

    用法二:二线程交替执行。

    public class CyclicBarrier_v3 { static CyclicBarrier barrier ; public static void main(String[] args) throws InterruptedException { barrier = new CyclicBarrier(1, () -> { System.out.println("A is OK !"); }); Thread t1 = new Thread( () -> { try { for(int i=0; i <10; i++) { System.out.println("T1 is OK !"); barrier.await(); } } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } ); t1.start(); } }

    用法场景:

    多个线程执行时需要相互等待,达到等待阈值时,触发新线程执行,可反复触发。可实现2个线程交替执行。

    注意:

    当使用CyclicBarrier做等待(await)的线程被其他线程执行Interrupt方法打断时,会抛出2个异常,InterruptedException和BrokenBarrierException,使栅栏失效。

    CountDownLatch 和 CyclicBarrier 区别

    CyclicBarrier不会阻塞主线程,CountDownLatch会。CyclicBarrier会阻塞子线程,CountDownLatch不会。由于CountDownLatch的CountDown方法不阻塞,所以CountDownLatch用于等待事件发生(当某个事件发生了可运行CountDown方法,且可多次调用)。而CyclicBarrier用于本线程等待其他线程(所有线程await相互等待,达到阈值后,结束等待,触发新线程)。CountDownLatch只能用一次,再用就得重新new;CyclicBarrier可以循环使用。
    Processed: 0.009, SQL: 9