• java.util.concurrent.Exchanger应用范例


    java.util.concurrent.Exchanger应用范例与原理浅析--转载

    一、简介
       Exchanger是自jdk1.5起开始提供的工具套件,一般用于两个工作线程之间交换数据。在本文中我将采取由浅入深的方式来介绍分析这个工具类。首先我们来看看官方的api文档中的叙述:

    A synchronization point at which threads can pair and swap elements within pairs. Each thread presents some object on entry to the exchange method, matches with a partner thread, and receives its partner's object on return. An Exchanger may be viewed as a bidirectional form of a SynchronousQueue. Exchangers may be useful in applications such as genetic algorithms and pipeline designs.

        在以上的描述中,有几个要点:

    • 此类提供对外的操作是同步的;
    • 用于成对出现的线程之间交换数据;
    • 可以视作双向的同步队列;
    • 可应用于基因算法、流水线设计等场景。

       接着看api文档,这个类提供对外的接口非常简洁,一个无参构造函数,两个重载的范型exchange方法:
    public V exchange(V x) throws InterruptedException
    public V exchange(V x, long timeout, TimeUnit unit) throws InterruptedException, TimeoutException
       从官方的javadoc可以知道,当一个线程到达exchange调用点时,如果它的伙伴线程此前已经调用了此方法,那么它的伙伴会被调度唤醒并与之进行对象交换,然后各自返回。如果它的伙伴还没到达交换点,那么当前线程将会被挂起,直至伙伴线程到达——完成交换正常返回;或者当前线程被中断——抛出中断异常;又或者是等候超时——抛出超时异常。

    二、一个简单的例子
    按照某大师的观点,行为知之先,在知道了Exchanger的大致用途并参阅了使用说明后,我们马上动手写个例子来跑一跑:

    import java.util.concurrent.Exchanger;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    import java.util.concurrent.TimeUnit;
    import org.apache.log4j.Logger;
    
    /**
     * @Title: ExchangerTest
     * @Description: Test class for Exchanger
     * @Company: CSAIR
     * @Author: lixuanbin
     * @Creation: 2014年12月14日
     * @Version:1.0
     */
    public class ExchangerTest {
        protected static final Logger log = Logger.getLogger(ExchangerTest.class);
        private static volatile boolean isDone = false;
    
        static class ExchangerProducer implements Runnable {
            private Exchanger<Integer> exchanger;
            private static int data = 1;
            ExchangerProducer(Exchanger<Integer> exchanger) {
                this.exchanger = exchanger;
            }
    
            @Override
            public void run() {
                while (!Thread.interrupted() && !isDone) {
                    for (int i = 1; i <= 3; i++) {
                        try {
                            TimeUnit.SECONDS.sleep(1);
                            data = i;
                            System.out.println("producer before: " + data);
                            data = exchanger.exchange(data);
                            System.out.println("producer after: " + data);
                        } catch (InterruptedException e) {
                            log.error(e, e);
                        }
                    }
                    isDone = true;
                }
            }
        }
    
        static class ExchangerConsumer implements Runnable {
            private Exchanger<Integer> exchanger;
            private static int data = 0;
            ExchangerConsumer(Exchanger<Integer> exchanger) {
                this.exchanger = exchanger;
            }
    
            @Override
            public void run() {
                while (!Thread.interrupted() && !isDone) {
                    data = 0;
                    System.out.println("consumer before : " + data);
                    try {
                        TimeUnit.SECONDS.sleep(1);
                        data = exchanger.exchange(data);
                    } catch (InterruptedException e) {
                        log.error(e, e);
                    }
                    System.out.println("consumer after : " + data);
                }
            }
        }
    
        /**
         * @param args
         */
        public static void main(String[] args) {
            ExecutorService exec = Executors.newCachedThreadPool();
            Exchanger<Integer> exchanger = new Exchanger<Integer>();
            ExchangerProducer producer = new ExchangerProducer(exchanger);
            ExchangerConsumer consumer = new ExchangerConsumer(exchanger);
            exec.execute(producer);
            exec.execute(consumer);
            exec.shutdown();
            try {
                exec.awaitTermination(30, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                log.error(e, e);
            }
        }
    }

       这大致可以看作是一个简易的生产者消费者模型,有两个任务类,一个递增地产生整数,一个产生整数0,然后双方进行交易。每次交易前的生产者和每次交易后的消费者都会sleep 1秒来模拟数据处理的消耗,并在交易前后把整数值打印到控制台以便检测结果。在这个例子里交易循环只执行三次,采用一个volatile boolean来控制交易双方线程的退出。
       我们来看看程序的输出:

    consumer before : 0
    producer before: 1
    consumer after : 1
    producer after: 0
    consumer before : 0
    producer before: 2
    producer after: 0
    consumer after : 2
    consumer before : 0
    producer before: 3
    producer after: 0
    consumer after : 3

        输出结果验证了以下两件事情:

    • exchange方法真的帮一对线程交换了数据;
    • exchange方法真的会阻塞调用方线程直至另一方线程参与交易。

       那么在中断和超时两种情况下程序的运行表现会是怎样呢?作为一个小练习,有兴趣的观众可以设想并编写测试用例覆盖验证之。接下来谈谈最近我在生产场景中对Exchanger的应用。

  • 相关阅读:
    软件开发项目文档模版
    Java Swing 非常漂亮的外观Nimbus使用方法
    周00王总监 宁波浦一的同仁们 有看见的进来
    冰川世界
    君住长江头,我住长江尾,日日思君不见君,共饮长江水
    可乐男孩被保送上大学
    变形金刚2
    爱无罪
    英语习惯口语简写
    看完了团长,我没有心理在去看《南京!南京!》
  • 原文地址:https://www.cnblogs.com/liclBlog/p/15349499.html
Copyright © 2020-2023  润新知