CountDownLatch、CyclicBarrier、Semaphore、Exchanger

CountDownLatch、CyclicBarrier、Semaphore、Exchanger

CountDownLatch:

允许N个线程等待其他线程完成执行。无法进行重复使用,只能用一次。

比如有2个任务A,它要等待其他4个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能了。

public class Test {

     public static void main(String[] args) {    

         final CountDownLatch latch = new CountDownLatch(2);

          

         new Thread(){

             public void run() {

                 try {

                     System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");

                    Thread.sleep(3000);

                    System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");

                    latch.countDown();

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

             };

         }.start();

          

         new Thread(){

             public void run() {

                 try {

                     System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");

                     Thread.sleep(3000);

                     System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");

                     latch.countDown();

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }

             };

         }.start();

          

         try {

             System.out.println("等待2个子线程执行完毕...");

            latch.await();

            System.out.println("2个子线程已经执行完毕");

            System.out.println("继续执行主线程");

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

     } 

}
线程Thread-0正在执行
线程Thread-1正在执行
等待2个子线程执行完毕...
线程Thread-0执行完毕
线程Thread-1执行完毕
2个子线程已经执行完毕
继续执行主线程

 

CyclicBarrier

实现让N个线程等待至某个状态(达到初始化数量值)之后再全部同时执行,并且要全部线程都执行cyclicBarrier.await();后才执行线程后续逻辑。

在初次的4个线程越过barrier状态后,又可以用来进行新一轮的使用,可重复使用。

比如聚餐,一个人先到了就开始等待,又来一个。。直到全部人到期后,这时才能吃饭,全部吃完后由一个人去结账(runable),最后每个人去做自己的事情。

public class Test {

    public static void main(String[] args) {

        int N = 4;

        CyclicBarrier barrier  = new CyclicBarrier(N);

        for(int i=0;i<N;i++)

            new Writer(barrier).start();

    } 

    static class Writer extends Thread{

        private CyclicBarrier cyclicBarrier;

        public Writer(CyclicBarrier cyclicBarrier) {

            this.cyclicBarrier = cyclicBarrier;

        }

 

        @Override

        public void run() {

            System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据...");

            try {

                Thread.sleep(5000);      //以睡眠来模拟写入数据操作

                System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕");

                cyclicBarrier.await();

            } catch (InterruptedException e) {

                e.printStackTrace();

            }catch(BrokenBarrierException e){

                e.printStackTrace();

            }

            System.out.println("所有线程写入完毕,继续处理其他任务...");

        }

    }

}
线程Thread-0正在写入数据...
线程Thread-3正在写入数据...
线程Thread-2正在写入数据...
线程Thread-1正在写入数据...
线程Thread-2写入数据完毕,等待其他线程写入完毕
线程Thread-0写入数据完毕,等待其他线程写入完毕
线程Thread-3写入数据完毕,等待其他线程写入完毕
线程Thread-1写入数据完毕,等待其他线程写入完毕
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...

  从上面输出结果可以看出,每个写入线程执行完写数据操作之后,就在等待其他线程写入操作完毕。

  当所有线程线程写入操作完毕之后,所有线程就继续进行后续的操作了。

public class Test {

    public static void main(String[] args) {

        int N = 4;

        CyclicBarrier barrier  = new CyclicBarrier(N,new Runnable() {

            @Override

            public void run() {

                System.out.println("当前线程"+Thread.currentThread().getName());    

            }

        });

         

        for(int i=0;i<N;i++)

            new Writer(barrier).start();

    } 

    static class Writer extends Thread{

        private CyclicBarrier cyclicBarrier;

        public Writer(CyclicBarrier cyclicBarrier) {

            this.cyclicBarrier = cyclicBarrier;

        }

 

        @Override

        public void run() {

            System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据...");

            try {

                Thread.sleep(5000);      //以睡眠来模拟写入数据操作

                System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕");

                cyclicBarrier.await();

            } catch (InterruptedException e) {

                e.printStackTrace();

            }catch(BrokenBarrierException e){

                e.printStackTrace();

            }

            System.out.println("所有线程写入完毕,继续处理其他任务...");

        }

    }

}
线程Thread-0正在写入数据...
线程Thread-1正在写入数据...
线程Thread-2正在写入数据...
线程Thread-3正在写入数据...
线程Thread-0写入数据完毕,等待其他线程写入完毕
线程Thread-1写入数据完毕,等待其他线程写入完毕
线程Thread-2写入数据完毕,等待其他线程写入完毕
线程Thread-3写入数据完毕,等待其他线程写入完毕
当前线程Thread-3
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...
所有线程写入完毕,继续处理其他任务...

从结果可以看出,当四个线程都到达barrier状态后,会从四个线程中选择一个线程去执行Runnable。

 

Semaphore

可以控同时访问的线程个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。

假若一个工厂有5台机器,但是有8个工人,一台机器同时只能被一个工人使用,只有使用完了,其他工人才能继续使用。那么我们就可以通过Semaphore来实现:

public class Test {

    public static void main(String[] args) {

        int N = 8;            //工人数

        Semaphore semaphore = new Semaphore(5); //机器数目

        for(int i=0;i<N;i++)

            new Worker(i,semaphore).start();

    } 

     

    static class Worker extends Thread{

        private int num;

        private Semaphore semaphore;

        public Worker(int num,Semaphore semaphore){

            this.num = num;

            this.semaphore = semaphore;

        }

         

        @Override

        public void run() {

            try {

                semaphore.acquire();

                System.out.println("工人"+this.num+"占用一个机器在生产...");

                Thread.sleep(2000);

                System.out.println("工人"+this.num+"释放出机器");

                semaphore.release();            

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

        }

    }

}
工人0占用一个机器在生产...
工人1占用一个机器在生产...
工人2占用一个机器在生产...
工人4占用一个机器在生产...
工人5占用一个机器在生产...
工人0释放出机器
工人2释放出机器
工人3占用一个机器在生产...
工人7占用一个机器在生产...
工人4释放出机器
工人5释放出机器
工人1释放出机器
工人6占用一个机器在生产...
工人3释放出机器
工人7释放出机器
工人6释放出机器

 

下面对上面说的三个辅助类进行一个总结:

  1)CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同:

    CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;

    而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;

    另外,CountDownLatch是不能够重用的,而CyclicBarrier是可以重用的。

  2)Semaphore其实和锁有点类似,它一般用于控制对某组资源的访问权限。

 

Exchanger

Exchanger可以在两个线程之间交换数据,只能是2个线程,他不支持更多的线程之间互换数据。

当线程A调用Exchange对象的exchange()方法后,他会陷入阻塞状态,直到线程B也调用了exchange()方法,然后以线程安全的方式交换数据,之后线程A和B继续运行。

public class ThreadLocalTest {  
  
    public static void main(String[] args) {  
        Exchanger<List<Integer>> exchanger = new Exchanger<>();  
        new Consumer(exchanger).start();  
        new Producer(exchanger).start();  
    }  
  
}  
  
class Producer extends Thread {  
    List<Integer> list = new ArrayList<>();  
    Exchanger<List<Integer>> exchanger = null;  
    public Producer(Exchanger<List<Integer>> exchanger) {  
        super();  
        this.exchanger = exchanger;  
    }  
    @Override  
    public void run() {  
        Random rand = new Random();  
        for(int i=0; i<10; i++) {  
            list.clear();  
            list.add(rand.nextInt(10000));  
            list.add(rand.nextInt(10000));  
            list.add(rand.nextInt(10000));  
            list.add(rand.nextInt(10000));  
            list.add(rand.nextInt(10000));  
            try {  
                list = exchanger.exchange(list);  
            } catch (InterruptedException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
        }  
    }  
}  
  
class Consumer extends Thread {  
    List<Integer> list = new ArrayList<>();  
    Exchanger<List<Integer>> exchanger = null;  
    public Consumer(Exchanger<List<Integer>> exchanger) {  
        super();  
        this.exchanger = exchanger;  
    }  
    @Override  
    public void run() {  
        for(int i=0; i<10; i++) {  
            try {  
                list = exchanger.exchange(list);  
            } catch (InterruptedException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
            }  
            System.out.print(list.get(0)+", ");  
            System.out.print(list.get(1)+", ");  
            System.out.print(list.get(2)+", ");  
            System.out.print(list.get(3)+", ");  
            System.out.println(list.get(4)+", ");  
        }  
    }  
}  

8041, 3278, 7654, 2981, 7529,
7091, 4699, 3542, 9739, 3587,
1981, 3427, 7698, 8519, 4319,
2119, 8179, 4338, 7676, 2617,
2772, 6843, 9765, 8948, 418,
7433, 7846, 9442, 6114, 6475,
9142, 4858, 6511, 6401, 9709,
6466, 9250, 3018, 8256, 8771,
80, 8762, 7367, 8664, 9053,
8008, 5456, 2018, 9806, 8194,

 

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/108907.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • VS Code注释快捷键[通俗易懂]

    VS Code注释快捷键[通俗易懂]单行注释:Ctrl+/或先按CTRL+K,再按CTRL+U取消单行注释:Ctrl+/或先按CTRL+U,再按CTRL+K块注释Alt+Shift+A

    2022年8月15日
    9
  • python numpy矩阵转置_python转制

    python numpy矩阵转置_python转制题目难度:★☆☆☆☆类型:几何、二维数组、数学给定一个矩阵A,返回A的转置矩阵。矩阵的转置是指将矩阵的主对角线翻转,交换矩阵的行索引与列索引。示例示例1输入:[[1,2,3],[4,5,6],[7,8,9]]输出:[[1,4,7],[2,5,8],[3,6,9]]示例2输入:[[1,2,3],[4,5,6]]输出:[[1,4],[2,5],[3,6]]提示1…

    2025年5月28日
    0
  • Pytorch(一) Pytorch 的安装[通俗易懂]

    Pytorch(一) Pytorch 的安装[通俗易懂]安装步骤1.检查是否有合适的GPU,若有安装Cuda与CuDNN2.下载pytorch安装文件,进行pytorch的安装检查电脑是否有合适的GPU在桌面上右击如果能找到NVIDA控制面板,则说明该电脑有GPU。控制面板如下,并通过查看系统信息获取支持的Cuda版本点击帮助->点击系统信息弹出下面的对话框,在驱动程序版本那一栏就能看到该计算机使用的驱动版本。…

    2022年6月24日
    27
  • android 反编译,反,注射LOG

    android 反编译,反,注射LOG

    2022年1月5日
    54
  • web前端表单制作_注册表单测试用例

    web前端表单制作_注册表单测试用例03- web表单测试

    2022年4月22日
    51
  • python语言变量命名规则[通俗易懂]

    python语言变量命名规则[通俗易懂]Python语言变量命名规则变量名只能包含字母、数字和下划线。(推荐学习:0基础入门python)变量名可以字母或下划线开头,但不能以数字开头。例如,可将变量命名为message_1,但不能将其命名为1_message。变量名不能包含空格,但可使用下划线来分隔其中的单词。例如,变量名greeting_message可行,但变量名greetingmessage会引发错误。不要将Pytho…

    2022年6月14日
    32

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号