[笔记][Java7并发编程实战手册]3.8 并发任务间的数据交换Exchanger

[笔记][Java7并发编程实战手册]系列目录


简介

  Exchanger 是一个同步辅助类,用于两个并发线程之间在一个同步点进行数据交换。
  允许两个线程在某一个点进行数据交换。


本章exchanger 使用心得总结

  1. 两个线程必须使用同一个Exchanger对象,且只能是两个线程间的数据交换
  2. exchanger.exchange(v)的时候,当前线程会被阻塞,直到另一个线程执行该方法,同时完成数据的交换
  3. 类似这种数据交换的,生产者线程一定要先生产数据,再交换数据,消费者线程一定要先交换数据,再消费数据,否则会出现少消费数据的现象

示例

场景描述:一对一的 生产者和消费者,生产者每次生产5个商品,然后消费者把空的商品容器和生产者交换。

/**
 * Created by zhuqiang on 2015/8/23 0023.
 */
public class Client {
    public static void main(String[] args) {
        Exchanger<ArrayList<String>> exchanger = new Exchanger<ArrayList<String>>();

        new Thread(new Producer(exchanger)).start();
        new Thread(new Consumer(exchanger)).start();

    }
}

// 生产者
class Producer implements Runnable {
    private ArrayList<String> goods = new ArrayList<String>();  //商品容器
    private Exchanger<ArrayList<String>> exchanger = new Exchanger<ArrayList<String>>();

    public Producer(Exchanger<ArrayList<String>> exchanger) {
        this.exchanger = exchanger;
    }

    @Override
    public void run() {
        for (int i = 0; i < 3; i++) { //生产3次
            System.out.println("------------------------生产者生产第 " + i + "次");
            for (int j = 0; j < 3; j++) { //每次生产3个商品
                String e = (long) (Math.random() * 1000) + "";
                goods.add(e);
                System.out.println("生产了商品:" + e);
            }
            try {
                goods = exchanger.exchange(goods);  //交换数据
                System.out.println("生产者:数据交换完毕:获得交换的商品容器大小:" + goods.size());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

//消费者
class Consumer implements Runnable {
    private ArrayList<String> goods = new ArrayList<String>();  //商品容器
    private Exchanger<ArrayList<String>> exchanger = new Exchanger<ArrayList<String>>();

    public Consumer(Exchanger<ArrayList<String>> exchanger) {
        this.exchanger = exchanger;
    }

    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {  //消费3次
            try {
                goods = exchanger.exchange(goods);  //交换数据
                System.out.println("消费者:数据交换完毕:获得交换的商品容器大小:" + goods.size());

                // 消费商品
                Iterator<String> it = goods.iterator();
                if (goods.size() > 0) {
                    System.out.println("*********************消费者消费第 " + i + "次");
                    while (it.hasNext()) {
                        String next = it.next();
                        System.out.println("消费了商品:" + next);
                        it.remove();  //移除消费了的商品
                    }
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

某一次运行结果:

------------------------生产者生产第 0次
生产了商品:727
生产了商品:671
生产了商品:345
生产者:数据交换完毕:获得交换的商品容器大小:0
------------------------生产者生产第 1次
生产了商品:67
生产了商品:466
生产了商品:207
消费者:数据交换完毕:获得交换的商品容器大小:3
*********************消费者消费第 0次
消费了商品:727
消费了商品:671
消费了商品:345
消费者:数据交换完毕:获得交换的商品容器大小:3
生产者:数据交换完毕:获得交换的商品容器大小:0
------------------------生产者生产第 2次
*********************消费者消费第 1次
消费了商品:67
消费了商品:466
消费了商品:207
生产了商品:38
生产了商品:355
生产了商品:24
生产者:数据交换完毕:获得交换的商品容器大小:0
消费者:数据交换完毕:获得交换的商品容器大小:3
*********************消费者消费第 2次
消费了商品:38
消费了商品:355
消费了商品:24

结果说明
看上面的结果,可以发现两个线程是并行执行的,只有在交换的时候,交换数据同步点。
是不是觉得看着不明所以,这样看
————————生产者生产第 0次
生产了商品:727

第0次生产商品是 727,
***********消费者消费第 0次
消费了商品:727

第0次消费的也是727, 这就说明了。两个线程之间交换数据是我们想要的结果。

假如:我们把 下面的执行顺序调换下,让consumer在前,结果你发现还是先生产后消费。这个是为什么呢?

    new Thread(new Producer(exchanger)).start();
    new Thread(new Consumer(exchanger)).start();

是因为:
1. 从java内存模型角度来说,上面两行代码没有数据依赖性(在实际执行的时候他们的顺序不一定是谁先谁后)
2. 原因就出现在exchanger.exchange(v)方法,就算是消费者先执行,仔细看代码,消费者会先执行exchanger.exchange(v)方法,如果之前没有线程执行该方法,那么消费者则会休眠等待生产者执行该方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值