• Java核心知识点学习----多线程 倒计时记数器CountDownLatch和数据交换的Exchanger


    本文将要介绍的内容都是Java5中的新特性,一个是倒计时记数器---CountDownLatch,另一个是用于线程间数据交换的Exchanger.

    一.CountDownLatch

    1.什么是CountDownLatch? 

    倒计时计数器,调用CountDownLatch对象的CountDown()方法就将计数器减一,当计数到达0时,则所有等待者或者全部等待者开始执行.

    2.如何用?

    new CountDownLatch(1);

    直接new,其构造函数必须传一个int类型的参数,参数的意思是:

    count the number of times countDown must be invoked before threads can pass through await

    大致可理解成,有一个门,有N个门闩,要想打开门必须把所有门闩都打开,对应到线程上是说在线程通过等待前必须要执行的倒计时操作.

    3.举例

    package com.amos.concurrent;
    
    import java.util.Random;
    import java.util.concurrent.CountDownLatch;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /** 
    * @ClassName: Count_Down_Latch_Test 
    * @Description: 倒计时学习
    * @author: amosli
    * @email:hi_amos@outlook.com
    * @date Apr 27, 2014 11:51:43 PM  
    */
    public class Count_Down_Latch_Test {
        public static void main(String[] args) {
            ExecutorService executorService = Executors.newCachedThreadPool();
            final CountDownLatch countdownOrder = new CountDownLatch(1);// an order
            final CountDownLatch countdownAnwser = new CountDownLatch(3);// anwser
            for (int i = 0; i < 3; i++) {
                Runnable runnable = new Runnable() {
                    public void run() {
                        try {
                            countdownOrder.await();
                            System.out.println("线程" + Thread.currentThread().getName() + " 正准备接受命令");
                            System.out.println("线程"+Thread.currentThread().getName()+" 已经接受命令!");
                            Thread.sleep(new Random().nextInt(1000));
                            System.out.println("线程"+Thread.currentThread().getName()+" 回应处理结果!");
                            countdownAnwser.countDown();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                };
                executorService.execute(runnable);//启动线程池
            }
                   
            
            try {
                Thread.sleep(new Random().nextInt(1000));
                System.out.println("线程"+Thread.currentThread().getName()+" 即将下达命令!!");
                countdownOrder.countDown();
                System.out.println("线程"+Thread.currentThread().getName()+" 已经下达命令,正在等待返回结果!");
                countdownAnwser.await();
                System.out.println("线程"+Thread.currentThread().getName()+" 已经收到所有处理结果!");
    
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
          }
    }

    1).效果如下图所示:

    2)程序说明

    首先是创建了一个可缓存的线程池--->接着,创建两个CountDownLatch类,一个赋值为1,一个赋值为3;----->然后,执行一个for循环,在循环中,首先是实现了一个Runnable接口,然后,将Runnable接口加入到线程池中; 其中Runnable接口,首先是等待计数器为0,然后如果为0那么将计数器2的值减一,每循环一次减一,当第三次循环时,线程执行完毕;----->在Runnable接口中等待计数器为0,整个程序无法向下走,这时main方法,即主线程执行CountDown方法,计数器减一-------->最后等待所有的线程都执行完毕,返回最终的结果.

    4.扩展--官方例子

    package com.amos.concurrent;
    
    import java.util.concurrent.CountDownLatch;
    
    public class CountDownLatchTest {
        public static void main(String[] args) {
            try {
                new CountDownLatchTest().new Driver().main();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        class Driver { // ...
            void main() throws InterruptedException {
                CountDownLatch startSignal = new CountDownLatch(1);
                CountDownLatch doneSignal = new CountDownLatch(3);
    
                for (int i = 0; i < 3; ++i)
                    // create and start threads
                new Thread(new worker(startSignal, doneSignal)).start();
                dosomethingelse(); // don't let run yet
                startSignal.countDown(); // let all threads proceed
                dosomethingelse();
                doneSignal.await(); // wait for all to finish
            }
    
            private void dosomethingelse() {
                System.out.println("dosomethingelse...");
            }
        }
    
        class worker implements Runnable {
            private final CountDownLatch startsignal;
            private final CountDownLatch donesignal;
    
            worker(CountDownLatch startsignal, CountDownLatch donesignal) {
                this.startsignal = startsignal;
                this.donesignal = donesignal;
            }
    
            public void run() {
                try {
                    startsignal.await();
                    dowork();
                    donesignal.countDown();
                } catch (Exception ex) {
                } // return;
            }
    
            void dowork() {
                System.out.println("dowork....");
            }
        }
    
    }
    View Code

    跟上面例子差不多,首先都是设置一个等待,然后再调用计数器减一,执行最后的操作.

    CountDownLatch很适用于跑步比赛,当发令枪一声令下,所有选手开始跑起来.

    二.Exchanger

    1.什么是Exchange?作用是什么?

    用于实现两人之间的数据交换,每个人在完成一定的事务后想与对方交换数据;只有两人见面才会有交换.就像是情人间的约会,不见不散.

    2.如何使用?

    new Exchanger<V>();

    这里用到了泛型,即可以指定任意格式的数据,基本类型,对象等等都可以.

    这里要注意的是线程要成对出现才能进行数据交换.用来交换的方法为exchange(x); 

    Parameters:
    x the object to exchange

    参数为要进行交换给对方的数据.

    3.举例:

    package com.amos.concurrent;
    
    import java.util.Random;
    import java.util.concurrent.Exchanger;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /** 
    * @ClassName: ExchangerTest 
    * @Description: 线程间的数据交换Exchanger
    * @author: amosli
    * @email:hi_amos@outlook.com
    * @date Apr 28, 2014 12:26:48 AM  
    */
    public class ExchangerTest {
        public static void main(String[] args) {
            final Exchanger<String> exchanger = new Exchanger<String>();
        
            ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
            
            //线程一
            newCachedThreadPool.execute(new Runnable() {
                public void run() {
                    try {
                        String data1="111";
                        System.out.println("线程:"+Thread.currentThread().getName()+" 要换出去的数据为:"+data1);
                        Thread.sleep(new Random().nextInt(1000));
                        String exchange = exchanger.exchange(data1);
                        System.out.println("线程:"+Thread.currentThread().getName()+" 换回来的数据为:"+exchange);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    
                }
            });
            
            //线程二
            newCachedThreadPool.execute(new Runnable() {
                public void run() {
                    try {
                        String data1="hi_amos";
                        System.out.println("线程:"+Thread.currentThread().getName()+" 要换出去的数据为:"+data1);
                        Thread.sleep(new Random().nextInt(1000));
                        String exchange = exchanger.exchange(data1);
                        System.out.println("线程:"+Thread.currentThread().getName()+" 换回来的数据为:"+exchange);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    
                }
            });
        }
    }

    这里只需要注意使用exchange()方法即可.

    效果:

    4.扩展---官方例子

    class FillAndEmpty {
       Exchanger exchanger = new Exchanger();
       DataBuffer initialEmptyBuffer = ... a made-up type
       DataBuffer initialFullBuffer = ...
    
       class FillingLoop implements Runnable {
         public void run() {
           DataBuffer currentBuffer = initialEmptyBuffer;
           try {
             while (currentBuffer != null) {
               addToBuffer(currentBuffer);
               if (currentBuffer.isFull())
                 currentBuffer = exchanger.exchange(currentBuffer);
             }
           } catch (InterruptedException ex) { ... handle ... }
         }
       }
    
       class EmptyingLoop implements Runnable {
         public void run() {
           DataBuffer currentBuffer = initialFullBuffer;
           try {
             while (currentBuffer != null) {
               takeFromBuffer(currentBuffer);
               if (currentBuffer.isEmpty())
                 currentBuffer = exchanger.exchange(currentBuffer);
             }
           } catch (InterruptedException ex) { ... handle ...}
         }
       }
    
       void start() {
         new Thread(new FillingLoop()).start();
         new Thread(new EmptyingLoop()).start();
       }
     }
     } 

    官方的例子,也比较简单,启动两个线程,然后调用exchange()方法进行两个线程间的数据交换.

  • 相关阅读:
    Network(树形dp)洛谷2899
    2590 树的统计
    LCT 最小生成树
    几种贪心小结
    snmp
    div页面跳转
    2017.11.2总结,回顾及成果
    2017.11.1知识总结及回顾
    check,form,单选框与复选框总结
    HTML空格字符
  • 原文地址:https://www.cnblogs.com/amosli/p/3695535.html
Copyright © 2020-2023  润新知