问:你知道 Java 的 Exchanger 吗?简单说说其特点及应用场景?

答:Exchanger 是 JDK 1.5 开始提供的一个用于两个工作线程之间交换数据的封装工具类,简单说就是一个线程在完成一定的事务后想与另一个线程交换数据,则第一个先拿出数据的线程会一直等待第二个线程,直到第二个线程拿着数据到来时才能彼此交换对应数据。其定义为 Exchanger<V> 泛型类型,其中 V 表示可交换的数据类型,对外提供的接口很简单,具体如下:

  • Exchanger():无参构造方法。
  • V exchange(V v):等待另一个线程到达此交换点(除非当前线程被中断),然后将给定的对象传送给该线程,并接收该线程的对象。
  • V exchange(V v, long timeout, TimeUnit unit):等待另一个线程到达此交换点(除非当前线程被中断或超出了指定的等待时间),然后将给定的对象传送给该线程,并接收该线程的对象。

可以看出,当一个线程到达 exchange 调用点时,如果其他线程此前已经调用了此方法,则其他线程会被调度唤醒并与之进行对象交换,然后各自返回;如果其他线程还没到达交换点,则当前线程会被挂起,直至其他线程到达才会完成交换并正常返回,或者当前线程被中断或超时返回。

  1. public class Test {
  2. static class Producer extends Thread {
  3. private Exchanger<Integer> exchanger;
  4. private static int data = 0;
  5. Producer(String name, Exchanger<Integer> exchanger) {
  6. super("Producer-" + name);
  7. this.exchanger = exchanger;
  8. }
  9. @Override
  10. public void run() {
  11. for (int i=1; i<5; i++) {
  12. try {
  13. TimeUnit.SECONDS.sleep(1);
  14. data = i;
  15. System.out.println(getName()+" 交换前:" + data);
  16. data = exchanger.exchange(data);
  17. System.out.println(getName()+" 交换后:" + data);
  18. } catch (InterruptedException e) {
  19. e.printStackTrace();
  20. }
  21. }
  22. }
  23. }
  24. static class Consumer extends Thread {
  25. private Exchanger<Integer> exchanger;
  26. private static int data = 0;
  27. Consumer(String name, Exchanger<Integer> exchanger) {
  28. super("Consumer-" + name);
  29. this.exchanger = exchanger;
  30. }
  31. @Override
  32. public void run() {
  33. while (true) {
  34. data = 0;
  35. System.out.println(getName()+" 交换前:" + data);
  36. try {
  37. TimeUnit.SECONDS.sleep(1);
  38. data = exchanger.exchange(data);
  39. } catch (InterruptedException e) {
  40. e.printStackTrace();
  41. }
  42. System.out.println(getName()+" 交换后:" + data);
  43. }
  44. }
  45. }
  46. public static void main(String[] args) throws InterruptedException {
  47. Exchanger<Integer> exchanger = new Exchanger<Integer>();
  48. new Producer("", exchanger).start();
  49. new Consumer("", exchanger).start();
  50. TimeUnit.SECONDS.sleep(7);
  51. System.exit(-1);
  52. }
  53. }

可以看到,其结果可能如下:

  1. Consumer- 交换前:0
  2. Producer- 交换前:1
  3. Consumer- 交换后:1
  4. Consumer- 交换前:0
  5. Producer- 交换后:0
  6. Producer- 交换前:2
  7. Producer- 交换后:0
  8. Consumer- 交换后:2
  9. Consumer- 交换前:0
  10. Producer- 交换前:3
  11. Producer- 交换后:0
  12. Consumer- 交换后:3
  13. Consumer- 交换前:0
  14. Producer- 交换前:4
  15. Producer- 交换后:0
  16. Consumer- 交换后:4
  17. Consumer- 交换前:0

可以看到,如上就是一种典型的使用场景,简单理解这东西就是一个并发协作的工具类而已。

本文参考自 Java/Android Exchanger 必知必会实战题分析

转:https://www.jianshu.com/p/990ae2ab1ae0