• 多线程之Exchanger


    前言

    今天我们来分享最后多线程最后一个工具类组件,之后我们会继续探索多线程的相关知识:线程池、并发容器和框架,然后就是总结和查漏补缺。

    今天的内容很简单,内容也不太多,但是应用场景很典型,可以解决我们实际开发中数据对比的应用需求,好了,我们直接开始吧。

    Exchanger

    exchanger也是jdk1.5引入的,主要用来解决线程之间数据交换的问题,和它的字面意思一样,exchanger主要是用来交换数据的,需要注意的是,交换数据的时候只能是两个(一对)线程两两交换,下面我们直接看示例代码:

    public class Example {
        private final static Exchanger<String> exchanger = new Exchanger<>();
    
        public static void main(String[] args) {
            ExecutorService executorService = Executors.newFixedThreadPool(4);
            executorService.execute(() -> {
                String taskStr = "10只羊";
                try {
                    System.out.println("我是task1,正在等待交换,我有" + taskStr );
                    String exchange = exchanger.exchange(taskStr);
                    System.out.println("交换完成,task1获得:" + exchange);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
    
            executorService.execute(() -> {
                String taskStr = "一头牛";
                try {
                    System.out.println("我是task2,正在等待交换,我有" + taskStr );
                    String exchange = exchanger.exchange(taskStr);
                    System.out.println("交换完成,task2获得:" + exchange);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
    
            executorService.execute(() -> {
                String taskStr = "50只鸡";
                try {
                    System.out.println("我是task3,正在等待交换,我有" + taskStr );
                    String exchange = exchanger.exchange(taskStr);
                    System.out.println("交换完成,task3获得:" + exchange);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            executorService.execute(() -> {
                String taskStr = "40只鸭";
                try {
                    System.out.println("我是task4,正在等待交换,我有" + taskStr );
                    String exchange = exchanger.exchange(taskStr);
                    System.out.println("交换完成,task4获得:" + exchange);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
    
            executorService.shutdown();
        }
    }
    

    在上面的代码中,我们定义了一个交换器Exchanger,它本身是支持泛型的,这里我们定义的是string,然后定义了一个线程池,通过线程池分别启动四个线程,在四个线程中,都有这样一行代码:

    String exchange = exchanger.exchange(taskStr);
    

    它的作用就是和其他线程交换数据,并拿到交换后的数据,然后我们运行示例代码:

    数据交换前,他们分别拥有10只羊,一头牛,50只鸡,40只鸭,交换后task2拿到task1的牛,task1拿到task2的羊,其他的也一样,之所以用这个例子,是因为它的交换过程确实很像原始社会的以物易物。

    这里需要注意的是,交换数据的线程数量必须为双数,否则线程会一直被exchange方法阻塞,这里我们把最后一个线程先删除掉,然后运行:

    因为没有线程再与task3进行数据交换,所以线程被阻塞了。

    当然有时候,阻塞并非是人为的,而是在某些特殊情况下发生,为了避免因为这种情况导致线程持续阻塞,我们可以用exchanger的另一个方法:

    这个方法支持设定超时时间,如果到设定时间依然没有数据交换,该方法会抛出TimeoutException异常:

    关于exchanger的应用场景,我能想到的就两个,一个就是数据校验,两个线程同时操作同一批数据,我们可以对数据最终的一致性做校验,互相验证,比如两个excel的数据比对;另外一个场景和这个很类似,就是我们在实际开发经常会遇到方法A的运行条件需要根据B的运行结果进行优化调整,这时候我们就可以通过exchanger来来进行数据交换,然后再继续触发相关操作。

    总结

    exchanger最大的优点是她可以在运行的过程中交换数据,其他的应用场景在遇到具体问题的时候再进一步分析吧。好了,exchanger的相关内容就到这里。

    今天还要补充一个小知识,是关于mysql的,是一个小知识点,也是线上发现的一个小问题,具体来说就是mysql的求和语句,如果求和字段的值全部为null,那么最终的求和结果是null,而不是0,这样就会有潜在的空指针异常:

    select course_type, sum(order_id) from course GROUP BY course_type
    

    因为order_id都是null,所以最终sum(order_id)就是null:

    这样如果你用包装类接收sum(order_id)就是null,后续在操作它的时候一定要做空指针校验,否则就是个线上bug。好了,就这么多,over

  • 相关阅读:
    python学习之函数基础
    Java并发编程之支持并发的list集合你知道吗
    Java并发编程之CAS第三篇-CAS的缺点及解决办法
    Java并发编程之CAS二源码追根溯源
    Java并发编程之CAS第一篇-什么是CAS
    Java并发编程之验证volatile指令重排-理论篇
    【免费百度网盘不限速】爱奇艺万能联播 百度网盘不限速的方法
    Java并发编程之验证volatile不能保证原子性
    Java并发编程之验证volatile的可见性
    Java并发编程学习前期知识下篇
  • 原文地址:https://www.cnblogs.com/caoleiCoding/p/15015064.html
Copyright © 2020-2023  润新知