Exchanger类
类Exchanger的功能可以使两个线程之间传输数据,它比生产者消费者模式使用的wait/notify更加方便。
Exchanger类可用于两个线程之间交换信息。可简单地将Exchanger对象理解为一个包含两个格子的容器,通过exchanger方法可以向两个格子中填充信息。当两个格子中的均被填充时,该对象会自动将两个格子的信息交换,然后返回给线程,从而实现两个线程的信息交换。
方法exchange()传递数据
V exchange(V x) 方法,用于两个线程交换数据,返回值V是取得的交换的数据,参数V是用于交换的数据。只有当两个线程都放入数据才会相互交互。
示例中创建了两个线程,线程在执行过程中,调用同一个exchanger对象的exchange方法,进行信息通信,当两个线程均已将信息放入到exchanger对象中时,exchanger对象会将两个线程放入的信息交换,然后返回。
public class ExchangeDemo {
public static class MyThread extends Thread{
//用于数据交换
private Exchanger<String> exchanger;
public MyThread(String name, Exchanger<String> exchanger) {
super(name);
this.exchanger = exchanger;
}
@Override
public void run() {
try {
//exchange()方法存放String用于跟其他线程交换,如果其他线程也在exchange()方法中存储了就会互相交换
//当前exchange()方法的参数是String类型,返回值也是String类型
System.out.println(Thread.currentThread().getName() + "取得其他的用于交换的数据:" + exchanger.exchange(Thread.currentThread().getName()+"存放的数据"));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Exchanger<String> exchanger = new Exchanger<String>();
MyThread myThreadA = new MyThread("线程A",exchanger);
MyThread myThreadB = new MyThread("线程B",exchanger);
myThreadA.start();
myThreadB.start();
}
}
线程B取得其他的用于交换的数据:线程A存放的数据
线程A取得其他的用于交换的数据:线程B存放的数据
方法exchange()堵塞的特性
如果只有一个线程执行了exchange()方法,没有另一个线程执行,会怎么样呢?该线程会一直等待,一直堵塞等待。
public class ExchangeDemo {
public static class MyThread extends Thread{
//用于数据交换
private Exchanger<String> exchanger;
public MyThread(String name, Exchanger<String> exchanger) {
super(name);
this.exchanger = exchanger;
}
@Override
public void run() {
try {
//exchange()方法存放String用于跟其他线程交换,如果其他线程也在exchange()方法中存储了就会互相交换
//当前exchange()方法的参数是String类型,返回值也是String类型
//如果没有其他线程跟其交换数据,会一直堵塞等待!
System.out.println(Thread.currentThread().getName() + "取得其他的用于交换的数据:" + exchanger.exchange(Thread.currentThread().getName()+"存放的数据"));
//不会执行,因为没有线程 跟其交换数据,会一直在上面的exchange()方法处堵塞等待
System.out.println(Thread.currentThread().getName()+"在等待数据交换");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
Exchanger<String> exchanger = new Exchanger<String>();
MyThread myThreadA = new MyThread("线程A",exchanger);
myThreadA.start();
Thread.sleep(1000);
System.out.println("线程启动了");
}
}
线程启动了
上面的线程一直堵塞等待其他线程跟其交换数据。
多个线程执行方法exchange()传递数据
下面的代码创建了3个线程,而线程的交换是两两交换的,所以奇数个线程,始终会有一个线程一直在等待交换,一直在堵塞,另外两个线程会完成交换。偶数线程不会存在这种情况,但是不管奇数个线程还是偶数个线程,线程的数据交互是随机,可能A跟B交换,可能A跟C交换。
public class ExchangeDemo {
public static class MyThread extends Thread{
//用于数据交换
private Exchanger<String> exchanger;
public MyThread(String name, Exchanger<String> exchanger) {
super(name);
this.exchanger = exchanger;
}
@Override
public void run() {
try {
//exchange()方法存放String用于跟其他线程交换,如果其他线程也在exchange()方法中存储了就会互相交换
//当前exchange()方法的参数是String类型,返回值也是String类型
//如果没有其他线程跟其交换数据,会一直堵塞等待!
System.out.println(Thread.currentThread().getName() + "取得其他的用于交换的数据:" + exchanger.exchange(Thread.currentThread().getName()+"存放的数据"));
//如果没有线程跟其交换数据,不会执行,因为没有线程 跟其交换数据,会一直在上面的exchange()方法处堵塞等待
System.out.println(Thread.currentThread().getName()+"完成数据交换");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
Exchanger<String> exchanger = new Exchanger<String>();
MyThread myThreadA = new MyThread("线程A",exchanger);
MyThread myThreadB = new MyThread("线程B",exchanger);
MyThread myThreadC = new MyThread("线程C",exchanger);
myThreadA.start();
myThreadB.start();
myThreadC.start();
}
}
线程B取得其他的用于交换的数据:线程C存放的数据
线程C取得其他的用于交换的数据:线程B存放的数据
线程B完成数据交换
线程C完成数据交换
本次执行,是BC交换数据,A线程一直在等待数据交换。
方法exchange(V x, long timeout, TimeUnit unit)与超时
当调用exchange(V x, long timeout, TimeUnit unit)方法后在指定的时间内没有其他线程获取数据,则出现超时异常。
public class ExchangeDemo {
public static class MyThread extends Thread{
//用于数据交换
private Exchanger<String> exchanger;
public MyThread(String name, Exchanger<String> exchanger) {
super(name);
this.exchanger = exchanger;
}
@Override
public void run() {
try {
//exchange()方法存放String用于跟其他线程交换,如果其他线程也在exchange()方法中存储了就会互相交换
//当前exchange()方法的参数是String类型,返回值也是String类型
//如果没有其他线程跟其交换数据,会一直堵塞等待!
System.out.println(Thread.currentThread().getName() + "取得其他的用于交换的数据:" + exchanger.exchange(Thread.currentThread().getName()+"存放的数据",3, TimeUnit.SECONDS));
//如果没有线程跟其交换数据,不会执行,因为没有线程 跟其交换数据,会一直在上面的exchange()方法处堵塞等待
System.out.println(Thread.currentThread().getName()+"完成数据交换");
} catch (InterruptedException | TimeoutException e) {
System.out.println(Thread.currentThread().getName()+"报超时异常");
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
Exchanger<String> exchanger = new Exchanger<String>();
MyThread myThreadA = new MyThread("线程A",exchanger);
MyThread myThreadB = new MyThread("线程B",exchanger);
MyThread myThreadC = new MyThread("线程C",exchanger);
myThreadA.start();
myThreadB.start();
myThreadC.start();
}
}
线程A取得其他的用于交换的数据:线程C存放的数据
线程A完成数据交换
线程C完成数据交换
线程B报超时异常
java.util.concurrent.TimeoutException
at java.base/java.util.concurrent.Exchanger.exchange(Exchanger.java:627)
at com.example.demo.ExchangeDemo$MyThread.run(ExchangeDemo.java:23)
总结
类Semaphore 的主要作用是限制并发执行的线程个数,它具有 synchronized 所不具有的强大功能,比如等待获得许可的同时可以加入等待时间,还有尝试是否可以持有锁等这类扩展功能,可以说 Semaphore 类是强有力的控制并发线程个数的解决方案之一,而 Exchanger是线程间传输数据的方式之 ,而且在传输的数据类型上并没有任何限制。