ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

多线程之Exchanger

2021-07-15 13:31:17  阅读:166  来源: 互联网

标签:Exchanger taskStr exchange 交换 线程 exchanger 多线程 String


前言

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

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

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

标签:Exchanger,taskStr,exchange,交换,线程,exchanger,多线程,String
来源: https://www.cnblogs.com/caoleiCoding/p/15015064.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有