JUC工具包(一) CountDownLatch与CyclicBarrier与Phaser

本文介绍了java.util.concurrent工具包中的CountDownLatch、CyclicBarrier和Phaser,详细阐述了它们的作用、构造函数、常用API及使用案例。CountDownLatch用于线程间的等待,CyclicBarrier则是一个可重用的同步辅助类,而Phaser结合了两者特性,支持父子结构,并能模拟CountDownLatch和CyclicBarrier的行为。
摘要由CSDN通过智能技术生成

java.util.concurrent工具包(一)


1. CountDownLatch

----作用----
  • countDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。

  • 是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。


----构造函数----
构造函数作用备注
public CountDownLatch(int count)初始化this.count = count的CountDownLatch如果count<0
会抛出IllegalArgumentException

----常用API----
API作用备注
void await() throws InterruptedException调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行阻塞方法,一直到count为0才退出
boolean await(long timeout, TimeUnit unit) throws InterruptedException和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行返回值(是否提前完成>>正常出口 true)
超时出去的 false
void countDown()将count自减如果count已经是0了依然能够调用,但是不会下减也不会报错
long getCount()获得当前count

----使用案例----
package myConcurrent.s2020_03_25;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

public class CountDownLatchExample {

    public static void main(String[] args) throws InterruptedException {
        int n = 4;
        final CountDownLatch countDownLatchLock = new CountDownLatch(n);
        IntStream.range(0, n).forEach(i -> {
            new Thread(() -> {
                System.out.println(Thread.currentThread() + " Working~2S!");
                try {
                    TimeUnit.SECONDS.sleep(20);
                    countDownLatchLock.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, "Thread-" + i).start();
        });

        boolean flag = countDownLatchLock.await(2,TimeUnit.SECONDS);
        System.out.println("是否正常完成?" + flag);
        System.out.println("等待的数量 : " + countDownLatchLock.getCount());

        countDownLatchLock.await();
        System.out.println("这次是真的完成了!");

        //尝试在下减一次
        countDownLatchLock.countDown();
        System.out.println("最后的需要等待的count " + countDownLatchLock.getCount());


    }

}

----输出----

Thread[Thread-0,5,main] Working~2S!
Thread[Thread-1,5,main] Working~2S!
Thread[Thread-2,5,main] Working~2S!
Thread[Thread-3,5,main] Working~2S!
是否正常完成?false
等待的数量 : 4
这次是真的完成了!
最后的需要等待的count 0


2. CyclicBarrier

----作用----

​ CyclicBarrier是一个同步辅助类,允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。


----构造函数----
构造函数作用备注
public CyclicBarrier(int parties, Runnable barrierAction)设置互相等待的线程数到达parties之后,会触发的一个线程 barrierAction如果parties<0 会抛出一个异常
public CyclicBarrier(int parties)会调用this(parties,null)

----常用API----
API作用备注
int getParties()获得CyclicBarrier打开屏障需要的数量
int getNumberWaiting()获得正在等待的线程数
int await()在CyclicBarrier上等待的线程数量达到parties,则所有线程被释放,继续执行。
当前线程被中断,则抛出InterruptedException异常,并停止等待,继续执行。
其他等待的线程被中断,则当前线程抛出BrokenBarrierException异常,并停止等待,继续执行。
其他等待的线程超时,则当前线程抛出BrokenBarrierException异常,并停止等待,继续执行。
其他线程调用CyclicBarrier.reset()方法,则当前线程抛出BrokenBarrierException异常,并停止等待,继续执行。
返回值是当前的线程第一个到达屏障的
int await(timeout,TimeUnit)几乎与await()相同,但是如果超时的话,会抛出TimeoutException同await()
boolean isBroken()如果屏障已经被打破 则返回true如果被调用了reset,该屏障会被修复
void reset()修复屏障, 重复使用

----使用案例----
  • Example1

    package myConcurrent.s2020_03_25;
    
    import java.util.concurrent.BrokenBarrierException;
    import java.util.concurrent.CyclicBarrier;
    import java.util.concurrent.TimeUnit;
    import java.util.stream.IntStream;
    
    /**
     * 测试只有 阈值的 屏障构造
     * */
    public class CyclicBarrierExample1 {
        public static void main(String[] args) throws BrokenBarrierException, InterruptedException {
            int n = 10;
            final CyclicBarrier cyclicBarrier = new CyclicBarrier(n + 1);
            IntStream.range(0, n).forEach(i -> {
                new Thread(() -> {
                    try {
                        TimeUnit.SECONDS.sleep(2);
                        int index = cyclicBarrier.await();
                        System.out.println(Thread.currentThread().getName() + " 是第 " + index + " 到达屏障的!");
                    } catch (InterruptedException e) {
                        System.out.println("线程被打断");
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        System.out.println("屏障被重置");
                        e.printStackTrace();
                    }
                }, "Thread-" + i) {
                }.start();
            });
    
            int mainIndex = cyclicBarrier.await();
            System.out.println("主线程是第 " + mainIndex + " 个到达屏障");
            System.out.println("屏障是否被打破了:" + cyclicBarrier.isBroken());
        }
    }
    
    
  • Example2

    package myConcurrent.s2020_03_25;
    
    import java.util.concurrent.BrokenBarrierException;
    import java.util.concurrent.CyclicBarrier;
    import java.util.concurrent.TimeUnit;
    import java.util.stream.IntStream;
    
    /**
     * 测试带回调的构造屏障
     * **/
    public class CyclicBarrierExample2 {
        public static void main(String[] args) {
            int n = 10;
            final CyclicBarrier cyclicBarrier = new CyclicBarrier(n, () -> {
                System.out.println("屏障已经被打破!");
            });
    
            IntStream.range(0, n).forEach(i -> {
                new Thread(() -> {
                    try {
                        TimeUnit.SECONDS.sleep(1);
                        cyclicBarrier.await();
                    } catch (InterruptedException | BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                }).start();
            });
        }
    }
    
    
  • Example3

    package myConcurrent.s2020_03_25;
    
    import java.util.concurrent.BrokenBarrierException;
    import java.util.concurrent.CyclicBarrier;
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.TimeoutException;
    import java.util.stream.IntStream;
    
    /**
     * 测试带有时间的await方法
     * 以及 reset 方法
     */
    public class CyclicBarrierExample3 {
        public static void main(String[] args) throws InterruptedException{
            int n = 10;
            final CyclicBarrier cyclicBarrier = new CyclicBarrier(n, () -> {
                System.out.println("屏障已经被打破");
            });
            System.out.println("等待中的线程数量:" + cyclicBarrier.getNumberWaiting());
            System.out.println("屏障打破需要的等待线程数量:" + cyclicBarrier.getParties());
            System.out.println("屏障是否被破坏" + cyclicBarrier.isBroken());
    
            IntStream.range(0, n-1).forEach(i -> {
                new Thread(() -> {
                    try {
                        cyclicBarrier.await(3L, TimeUnit.SECONDS);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        System.out.println("由于一个线程的超时错误! 导致屏障已经毁坏!");
    //                    e.printStackTrace();
                    } catch (TimeoutException e) {
                        System.out.println(Thread.currentThread().getName() + " 等待已经超时了-----");
    //                    e.printStackTrace();
                    }
                },"Thread-" + i).start();
            });
    
            TimeUnit.SECONDS.sleep(4);
    
            System.out.println(">>>  屏障是否坏了 : " + cyclicBarrier.isBroken());
    
            try {
                cyclicBarrier.await();
            } catch (BrokenBarrierException e) {
                System.out.println("屏障已经坏了!");
            }
    
    
            //恢复 屏障
            cyclicBarrier.reset();
    
            System.out.println("等待中的线程数量:" + cyclicBarrier.getNumberWaiting());
            System.out.println("屏障打破需要的等待线程数量:" + cyclicBarrier.getParties());
            System.out.println("屏障是否被破坏" + cyclicBarrier.isBroken());
        }
    }
    
    


3. CountDownLatch 和 CyclicBarrier 的区别

  1. countDownLatch是一个计数器,线程完成一个记录一个,计数器递减,只能只用一次
  2. CyclicBarrier的计数器更像一个阀门,需要所有线程都到达,然后继续执行,计数器递增,提供reset功能,可以多次使用
  3. CyClicBarrier 是需要 线程相互协作的,而CountDownLatch 不需要线程相互协作,只要不断的 countDown()


4. Phaser ( JDK1.7 )

-----杂项-----
  • 同时拥有着 CountDownLatch 和 CyclicBarrier 的特性
  • 能够形成父子结构的关系
  • 一旦形成父子结构 , 一个相位器的关闭,会导致所有相位器的关闭
  • 一个相位器被注册为父相位器,如果子相位器的parties不为0,那么 父相位器 的parties 会+1
  • 子相位器相位改变一次之后,就不能够再arrive,否则会抛出异常, 同时 父相位的到达数会+1

-----构造函数-----
构造函数作用备注
Phaser()构造一个没有父Phaser和0个证书的phaser
Phaser(int parties)创建一个parties个证书的phaser<0 或者
大于 1<<16 都会报错
Phaser(Phaser parent)创建一个带有父的phaser
Phaser(Phaser parent, int parties)主要的实例方法

-----常用API-----
API作用备注
int arrive()表示该线程已经到达了目标, 相位器 旋转了1 , 返回值是 当前的相位,如果该相位器已经终结,那么返回值就是一个负数的不会发生阻塞
int arriveAndAwaitAdvance()到达并且等待其他线程,到达屏障的阀值,就能打破内存屏障,返回值 是下一个相位 ( 但是不是精确值 ,受到多线程的 影响),如果 该相位器关闭,则返回一个负数阻塞方法
int arriveAndDeregister()到达并且注销一个值
返回值同上
非阻塞方法
int awaitAdvance(int phase)等待当前相位器不是phase阶段的时候返回当前阶段
如果phase不为当前阶段,那么就不会阻塞
通常用来判断该阶段是否已经完成
根据参数
是否会发生阻塞
int awaitAdvanceInterruptibly(int phase)throws InterruptedException作用同上,但是能够响应中断
int awaitAdvanceInterruptibly(int phase, long timeout, TimeUnit unit) throws InterruptedException, TimeoutException作用同上一个方法, 再这个基础上加上了时间限制
int register()注册一个parties… 相位器的parties +1
返回值依然是 当前的相位
如果返回值是 一个负数 意味着 相位器已经终结
int bulkRegister(int parties)作用同上一个方法, 批量注册parties如果为负数,会抛出异常
void forceTermination()关闭相位器会造成所有子相位器或者父相位器全部关闭
int getPhase()返回当前相位器的 相位

-----代码示例-----
  1. 实例化方法示例

    package myConcurrent.s2020_03_27;
    
    import java.util.concurrent.Phaser;
    
    public class PhaserExample1 {
        public static void main(String[] args) throws InterruptedException {
            //演示实例化
            Phaser phaser1 = new Phaser();
            Phaser phaser2 = new Phaser(1);
            Phaser phaser3 = new Phaser(phaser2);
            Phaser phaser4 = new Phaser(phaser2,2);
    
            System.out.println(phaser1);
            System.out.println(phaser2);
            System.out.println(phaser3);
            System.out.println(phaser4);
        }
    }
    

  1. 测试 各种 arrive方法

    package myConcurrent.s2020_03_27;
    
    import java.util.concurrent.Phaser;
    import java.util.concurrent.TimeUnit;
    
    public class PhaserExample2 {
        public static void main(String[] args) throws InterruptedException {
            final Phaser phaser = new Phaser(2);
    
            //测试arrive
            new ArriveThread(phaser);
            mySleep(1); //停顿一会
    
            System.out.println("========================");
            System.out.println("> 当前的相位 : " + phaser.getPhase());
            new ArriveThread(phaser);
            mySleep(1);
    
            System.out.println("> 当前的相位 : " + phaser.getPhase());
    
            //测试arriveAndWait
            new ArriveAndWaitThread(phaser);
            mySleep(1);
    
            System.out.println("========================");
    
            new ArriveAndWaitThread(phaser);
            mySleep(1);
    
            System.out.println("> 当前的相位 : " + phaser.getPhase());
    
            //测试arriveAndDeRegister
            System.out.println("> 当前的相位器注册了 : " + phaser.getRegisteredParties() +" 个部分");
            new ArriveAndDeRegisterThread(phaser);
            mySleep(1);
    
            System.out.println("====================");
    
            new ArriveAndDeRegisterThread(phaser);
            mySleep(1);
            System.out.println("> 当前的相位器注册了 : " + phaser.getRegisteredParties() +" 个部分");
            
        }
    
        public static class ArriveAndDeRegisterThread extends Thread{
            private final Phaser phaser;
    
            public ArriveAndDeRegisterThread(Phaser phaser) {
                this.phaser = phaser;
                this.start();
            }
    
            @Override
            public void run() {
                phaser.arriveAndDeregister();
                System.out.println("<< 我不会阻塞,而且能够取消掉一个注册信息!");
            }
        }
    
    
        public static class ArriveAndWaitThread extends Thread{
            private final Phaser phaser;
    
            public ArriveAndWaitThread(Phaser phaser) {
                this.phaser = phaser;
                this.start();
            }
    
            @Override
            public void run() {
                phaser.arriveAndAwaitAdvance();
                System.out.println("<< 我会和其他的线程一起输出~");
            }
        }
    
    
        public static class ArriveThread extends Thread {
            private final Phaser phaser;
    
            public ArriveThread(Phaser phaser) {
                this.phaser = phaser;
                this.start();
            }
    
            @Override
            public void run() {
                phaser.arrive();
                System.out.println("<< 我没有被阻塞");
            }
        }
    
        private static void mySleep(long seconds) throws InterruptedException {
            TimeUnit.SECONDS.sleep(seconds);
        }
    }
    
    

  1. 测试各种 awit 相关的方法
package myConcurrent.s2020_03_27;

import java.util.concurrent.Phaser;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.IntStream;

//测试各种 await方法
public class PhaserExample3 {
    public static void main(String[] args) throws InterruptedException {
        Phaser phaser = new Phaser(2);
        IntStream.range(0, 2).forEach(i -> {
            new Thread(
                    () -> {
                        phaser.arrive();
                        System.out.println(i + " >> 已经完成了工作!");
                    }
            ).start();
        });

        int nowPhase = phaser.awaitAdvance(phaser.getPhase());
        System.out.println("当前第一阶段工作已经完成!");
        System.out.println("当前相位器的相位是 : " + nowPhase);

        Thread dealWait = new Thread(
                () -> {
                    try {
                        phaser.awaitAdvanceInterruptibly(phaser.getPhase());
                    } catch (InterruptedException e) {
                        System.out.println(">>>  等待能够响应中断");
                    }
                }
        );
        dealWait.start();
        mySleep(1);
        dealWait.interrupt();       //尝试中断

        try {
            phaser.awaitAdvanceInterruptibly(phaser.getPhase(),2,TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            System.out.println(">> 我能够等了一会就不等了");
        }
    }

    private static void mySleep(long seconds) throws InterruptedException {
        TimeUnit.SECONDS.sleep(seconds);
    }
}


  1. 测试一些父子关系的相位器,以及相位器的关闭

    package myConcurrent.s2020_03_27;
    
    import java.util.concurrent.Phaser;
    import java.util.concurrent.TimeUnit;
    import java.util.stream.IntStream;
    
    public class PhaserExample4 {
        public static void main(String[] args) throws InterruptedException {
            final Phaser phaser = new Phaser(2);
            final Phaser son = new Phaser(phaser, 2);
    
            // 子相位器的parties不为0 那么 父相位器的parties 会+1
            System.out.println("father phaser is " + phaser.getRegisteredParties());
    
            //phaser到达3次能够改变相位
            System.out.println("father 当前相位是 " + phaser.getPhase());
            IntStream.range(0, 3).forEach(i -> {
                phaser.arrive();
            });
            System.out.println("father 当前相位是 " + phaser.getPhase());
    
            //子相位器相位改变一次, 父相位器的 到达数 能够增加1
            System.out.println("father 当前的到达数是 " + phaser.getArrivedParties());
    
            IntStream.range(0, 2).forEach(i -> {
                son.arrive();
            });
            System.out.println("father 当前的到达数是 " + phaser.getArrivedParties());
    
    
            //子相位器相位改变一次 再arrive 会报错
            try {
                IntStream.range(0, 3).forEach(i -> {
                    son.arrive();
                });
            } catch (Exception e) {
                System.out.println(" 报错了!");
            }
    
    
            //如果 线程 被阻塞的时候 终结 这个 相位器
            //将会不等待
            new Thread(
                    phaser::arriveAndAwaitAdvance
            ).start();
    
            TimeUnit.SECONDS.sleep(1);
            phaser.forceTermination();
    
            //已经被终结的相位器,再arrive,其他操作都会无效
            System.out.println(phaser);
            phaser.arriveAndDeregister();
            System.out.println(phaser);
        }
    }
    
    

  2. 相位器模拟CountDownLatch

    package myConcurrent.s2020_03_27;
    
    import java.util.concurrent.Phaser;
    import java.util.concurrent.TimeUnit;
    import java.util.stream.IntStream;
    
    public class PhaserExample5 {
        public static void main(String[] args) {
            final Phaser asCountDownLatch = new Phaser(3);
            IntStream.range(0, 3).forEach(
                    i -> new Thread(() -> {
                        System.out.println(i + "正在工作!");
                        try {
                            TimeUnit.SECONDS.sleep(1);
                            System.out.println(i + "工作完了");
                            asCountDownLatch.arrive();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }).start());
    
            asCountDownLatch.awaitAdvance(asCountDownLatch.getPhase());
            System.out.println("第一阶段的所有工作完成了!");
        }
    }
    
    

  3. 模拟CyclicBarrier

    package myConcurrent.s2020_03_27;
    
    
    import java.util.concurrent.Phaser;
    import java.util.stream.IntStream;
    
    public class PhaserExample6 {
        public static void main(String[] args) {
            final Phaser asCyclicBarrier = new Phaser(3);
            IntStream.range(0, 3).forEach(
                    i -> new Thread(
                            () -> {
                                System.out.println(i + "完成工作! 等待其他的线程");
                                asCyclicBarrier.arriveAndAwaitAdvance();
                                System.out.println("屏障已经打开!");
                            }
                    ).start()
            );
        }
    }
    
    


~谢谢观看

来自一个萌新的日常总结

欢迎大佬来拍砖

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值