Java并发新构件之Exchanger

Wesley13
• 阅读 486

    Exchanger是在两个任务之间交换对象的栅栏。当两个任务进入栅栏时,它们各自拥有一个对象,当它们离开时,它们都拥有对方的对象。Exchanger的典型应用场景是:一个任务在创建对象,而这些对象的生产代价很高,另一个任务在消费这些对象。通过这种方式,可以有更多的对象在被创建的同时被消费。

    为了演示Exchanger类,我们将创建生产者和消费者任务。ExchangerProducer和ExchangerConsumer使用一个List作为要求交换的对象,它们都包含一个用于这个List的Exchanger。当你调用Exchanger.exchange()方法时,它将阻塞直至对方任务调用它自己的exchange()方法,那时,这两个exchange()方法将同时完成,而List被交换:

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

class ExchangerProducer implements Runnable {
    private List<Fat> holder;
    private Exchanger<List<Fat>> exchanger;
    public ExchangerProducer(Exchanger<List<Fat>> exchanger, List<Fat> holder) {
        this.exchanger = exchanger;
        this.holder = holder;
    }
    @Override
    public void run() {
        try {
            while(!Thread.interrupted()) {
                //填充列表
                for (int i = 0;i < ExchangerDemo.size; i++) {
                    holder.add(new Fat());
                }
                //等待交换
                holder = exchanger.exchange(holder);
            }
        } catch (InterruptedException e) {
        }
        System.out.println("Producer stopped.");
    }
}

class ExchangerConsumer implements Runnable {
    private List<Fat> holder;
    private Exchanger<List<Fat>> exchanger;
    private volatile Fat value;
    private static int num = 0;
    public ExchangerConsumer(Exchanger<List<Fat>> exchanger, List<Fat> holder) {
        this.exchanger = exchanger;
        this.holder = holder;
    }
    @Override
    public void run() {
        try {
            while(!Thread.interrupted()) {
                //等待交换
                holder = exchanger.exchange(holder);
                //读取列表并移除元素
                for (Fat x : holder) {
                    num++;
                    value = x;
                    //在循环内删除元素,这对于CopyOnWriteArrayList是没有问题的
                    holder.remove(x);
                }
                if (num % 10000 == 0) {
                    System.out.println("Exchanged count=" + num);
                }
            }
        } catch (InterruptedException e) {
            
        }
        System.out.println("Consumer stopped. Final value: " + value);
    }
}

public class ExchangerDemo {
    static int size = 10;
    static int delay = 5; //秒
    public static void main(String[] args) throws Exception {
        ExecutorService exec = Executors.newCachedThreadPool();
        List<Fat> producerList = new CopyOnWriteArrayList<>();
        List<Fat> consumerList = new CopyOnWriteArrayList<>();
        Exchanger<List<Fat>> exchanger = new Exchanger<>();
        exec.execute(new ExchangerProducer(exchanger, producerList));
        exec.execute(new ExchangerConsumer(exchanger, consumerList));
        TimeUnit.SECONDS.sleep(delay);
        exec.shutdownNow();
    }
}

class Fat {
    private volatile double d;
    private static int counter = 1;
    private final int id = counter++;
    public Fat() {
        //执行一段耗时的操作
        for (int i = 1; i<10000; i++) {
            d += (Math.PI + Math.E) / (double)i;
        }
    }
    public void print() {System.out.println(this);}
    public String toString() {return "Fat id=" + id;}
}

执行结果(可能的结果):

Exchanged count=10000
Exchanged count=20000
Exchanged count=30000
Exchanged count=40000
Exchanged count=50000
Exchanged count=60000
Exchanged count=70000
Exchanged count=80000
Consumer stopped. Final value: Fat id=88300
Producer stopped.

    在main()中,创建了用于两个任务的单一的Exchanger,以及两个用于互换的CopyOnWriteArrayList。这个特定的List变体允许列表在被遍历的时候调用remove()方法,而不会抛出ConcurrentModifiedException异常。ExchangerProducer将填充这个List,然后将这个满列表跟ExchangerConsumer的空列表交换。交换之后,ExchangerProducer可以继续的生产Fat对象,而ExchangerConsumer则开始使用满列表中的对象。因为有了Exchanger,填充一个列表和消费另一个列表便同时发生了。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Wesley13 Wesley13
2年前
java 复制Map对象(深拷贝与浅拷贝)
java复制Map对象(深拷贝与浅拷贝)CreationTime2018年6月4日10点00分Author:Marydon1.深拷贝与浅拷贝  浅拷贝:只复制对象的引用,两个引用仍然指向同一个对象
Karen110 Karen110
2年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
java8新特性
Stream将List转换为Map,使用Collectors.toMap方法进行转换背景:User类,类中分别有id,name,age三个属性。List集合,userList,存储User对象1、指定keyvalue,value是对象中的某个属性值。 Map<Integer,StringuserMap1userList.str
Stella981 Stella981
2年前
JS 对象数组Array 根据对象object key的值排序sort,很风骚哦
有个js对象数组varary\{id:1,name:"b"},{id:2,name:"b"}\需求是根据name或者id的值来排序,这里有个风骚的函数函数定义:function keysrt(key,desc) {  return function(a,b){    return desc ? ~~(ak
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
unity将 -u4E00 这种 编码 转汉字 方法
 unity中直接使用 JsonMapper.ToJson(对象),取到的字符串,里面汉字可能是\\u4E00类似这种其实也不用转,服务器会通过类似fastjson发序列化的方式,将json转对象,获取对象的值就是中文但是有时服务器要求将传参中字符串中类似\\u4E00这种转汉字,就需要下面 publ
Stella981 Stella981
2年前
Clone
Java中对象的创建clone顾名思义就是复制,在Java语言中,clone方法被对象调用,所以会复制对象。所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象。那么在java语言中,有几种方式可以创建对象呢?1使用new操作符创建一个对象2使用clone方法复制一个对象那么这两种方
京东云开发者 京东云开发者
6个月前
Java服务总在半夜挂,背后的真相竟然是... | 京东云技术团队
最近有用户反馈测试环境Java服务总在凌晨00:00左右挂掉,用户反馈Java服务没有定时任务,也没有流量突增的情况,Jvm配置也合理,莫名其妙就挂了