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)
上一篇 2021年9月17日 上午8:00
下一篇 2021年9月17日 上午9:00


相关推荐

  • 平分石头

    平分石头平分石头题目描述:有一n个石头,它们的重量分别是W1,…,Wn.写一个程序,把它们分成两堆,使得两堆石头的重量差最小。输入格式:输入n(1≤n≤20)表示石头的个数。第二行n个石头的重量W1,…,Wn(1≤Wi≤100000)输出格式:输入最小的差(绝对值)样例输入:5581327…

    2022年7月24日
    10
  • Jetson Orin Nano上OpenCV4.8升级教程:从换镜像源到编译测试全攻略

    Jetson Orin Nano上OpenCV4.8升级教程:从换镜像源到编译测试全攻略

    2026年3月15日
    2
  • Flask 框架简单入门

    Flask 框架简单入门Flask框架介绍Flask框架什么是Flask?flask是一种基于python,并且依赖于Jinja2模板引擎(提供网页解析)和WerkzeugWSGI服务(pythonweb应用与web服务之间的接口)的一种微型框架。其中:Jinja2模板引擎:通俗来讲就是服务器接收到用户请求之后,将数据传入HTML文件中后,经过模板引擎的渲染将其呈现在网页中响应给用户。WerkzeugWSGI:pythonweb应用程序是一个被调用的对象,它无法直接与web服务器直接建立联系,所以WS

    2025年11月11日
    6
  • 数据库ER图基础概念整理

    数据库ER图基础概念整理什么是ER图?ER图即是实体关系图!ER图分为实体、属性、关系三个核心部分。实体是长方形体现,而属性则是椭圆形,关系为菱形。ER图中关联关系有三种:1对1(1:1):1对1关系是指对于实体集A与实体集B,A中的每一个实体至多与B中一个实体有关系;反之,在实体集B中的每个实体至多与实体集A中一个实体有关系。1对多(1:N):1对多关系是指实体集A与实体集B中至

    2022年6月21日
    28
  • 断点是什么,断点有哪几种类型?

    断点是什么,断点有哪几种类型?断点是在特定点暂停程序执行的特殊标记 使用断点可以使你检查当前程序状态和行为

    2026年3月18日
    2
  • accessors 作用_@Accessors介绍配置getter和setter

    accessors 作用_@Accessors介绍配置getter和setter前言今天在看 GitHub 某大神写的代码的时候看到一个标签并没有使用过 所有百度了一下使用方法在此记录一下 AccessorsAcc 的中文含义是存取器 Accessors 用于配置 getter 和 setter 方法的生成结果 源代码 Target ElementType TYPE ElementType FIELD Retention RetentionPol SOURCE

    2026年3月18日
    2

发表回复

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

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