JUC并发工具类--Exchanger
简介
Exchanger是一个用于线程间协作的工具类,用于两个线程间交换数据
。具体交换数据是通过exchange方法来实现的,如果一个线程先执行exchange方法,那么它会同步等待另一个线程也执行exchange方法,这个时候两个线程就都达到了同步点,两个线程就可以交换数据。
常用API
构造方法
/**
* Creates a new Exchanger.
*/
public Exchanger() {
participant = new Participant();
}
exchange()
线程数据交换。
/**
* Waits for another thread to arrive at this exchange point (unless
* the current thread is {@linkplain Thread#interrupt interrupted}),
* and then transfers the given object to it, receiving its object
* in return.
*
* <p>If another thread is already waiting at the exchange point then
* it is resumed for thread scheduling purposes and receives the object
* passed in by the current thread. The current thread returns immediately,
* receiving the object passed to the exchange by that other thread.
*
* <p>If no other thread is already waiting at the exchange then the
* current thread is disabled for thread scheduling purposes and lies
* dormant until one of two things happens:
* <ul>
* <li>Some other thread enters the exchange; or
* <li>Some other thread {@linkplain Thread#interrupt interrupts}
* the current thread.
* </ul>
* <p>If the current thread:
* <ul>
* <li>has its interrupted status set on entry to this method; or
* <li>is {@linkplain Thread#interrupt interrupted} while waiting
* for the exchange,
* </ul>
* then {@link InterruptedException} is thrown and the current thread's
* interrupted status is cleared.
*
* @param x the object to exchange
* @return the object provided by the other thread
* @throws InterruptedException if the current thread was
* interrupted while waiting
*/
@SuppressWarnings("unchecked")
public V exchange(V x) throws InterruptedException {
Object v;
Object item = (x == null) ? NULL_ITEM : x; // translate null args
if ((arena != null ||
(v = slotExchange(item, false, 0L)) == null) &&
((Thread.interrupted() || // disambiguates null return
(v = arenaExchange(item, false, 0L)) == null)))
throw new InterruptedException();
return (v == NULL_ITEM) ? null : (V)v;
}
/**
* Waits for another thread to arrive at this exchange point (unless
* the current thread is {@linkplain Thread#interrupt interrupted} or
* the specified waiting time elapses), and then transfers the given
* object to it, receiving its object in return.
*
* <p>If another thread is already waiting at the exchange point then
* it is resumed for thread scheduling purposes and receives the object
* passed in by the current thread. The current thread returns immediately,
* receiving the object passed to the exchange by that other thread.
*
* <p>If no other thread is already waiting at the exchange then the
* current thread is disabled for thread scheduling purposes and lies
* dormant until one of three things happens:
* <ul>
* <li>Some other thread enters the exchange; or
* <li>Some other thread {@linkplain Thread#interrupt interrupts}
* the current thread; or
* <li>The specified waiting time elapses.
* </ul>
* <p>If the current thread:
* <ul>
* <li>has its interrupted status set on entry to this method; or
* <li>is {@linkplain Thread#interrupt interrupted} while waiting
* for the exchange,
* </ul>
* then {@link InterruptedException} is thrown and the current thread's
* interrupted status is cleared.
*
* <p>If the specified waiting time elapses then {@link
* TimeoutException} is thrown. If the time is less than or equal
* to zero, the method will not wait at all.
*
* @param x the object to exchange
* @param timeout the maximum time to wait
* @param unit the time unit of the {@code timeout} argument
* @return the object provided by the other thread
* @throws InterruptedException if the current thread was
* interrupted while waiting
* @throws TimeoutException if the specified waiting time elapses
* before another thread enters the exchange
*/
@SuppressWarnings("unchecked")
public V exchange(V x, long timeout, TimeUnit unit)
throws InterruptedException, TimeoutException {
Object v;
Object item = (x == null) ? NULL_ITEM : x;
long ns = unit.toNanos(timeout);
if ((arena != null ||
(v = slotExchange(item, true, ns)) == null) &&
((Thread.interrupted() ||
(v = arenaExchange(item, true, ns)) == null)))
throw new InterruptedException();
if (v == TIMED_OUT)
throw new TimeoutException();
return (v == NULL_ITEM) ? null : (V)v;
}
参数:x
交换的共享数据。
参数:timeout
阻塞等待时间。
参数:unit
阻塞等待时间单位。
应用场景
模拟交易场景
import java.util.concurrent.Exchanger;
public class ExchangerDemo {
private static Exchanger exchanger = new Exchanger();
static String goods = "电脑";
static String money = "$4000";
public static void main(String[] args) throws InterruptedException {
System.out.println("准备交易,一手交钱一手交货...");
// 卖家
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("卖家到了,已经准备好货:" + goods);
try {
String money = (String) exchanger.exchange(goods);
System.out.println("卖家收到钱:" + money);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
// 买家
new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("买家到了,已经准备好钱:" + money);
String goods = (String) exchanger.exchange(money);
System.out.println("买家收到货:" + goods);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
}
模拟队列中交换数据场景
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Exchanger;
import java.util.UUID;
public class ExchangerDemo3 {
private static ArrayBlockingQueue<String> fullQueue
= new ArrayBlockingQueue<>(5);
private static ArrayBlockingQueue<String> emptyQueue
= new ArrayBlockingQueue<>(5);
private static Exchanger<ArrayBlockingQueue<String>> exchanger
= new Exchanger<>();
public static void main(String[] args) {
new Thread(new Producer()).start();
new Thread(new Consumer()).start();
}
/**
* 生产者
*/
static class Producer implements Runnable {
@Override
public void run() {
ArrayBlockingQueue<String> current = emptyQueue;
try {
while (current != null) {
String str = UUID.randomUUID().toString();
try {
current.add(str);
System.out.println("producer:生产了一个序列:" + str + ">>>>>加入到交换区");
Thread.sleep(2000);
} catch (IllegalStateException e) {
System.out.println("producer:队列已满,换一个空的");
current = exchanger.exchange(current);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 消费者
*/
static class Consumer implements Runnable {
@Override
public void run() {
ArrayBlockingQueue<String> current = fullQueue;
try {
while (current != null) {
if (!current.isEmpty()) {
String str = current.poll();
System.out.println("consumer:消耗一个序列:" + str);
Thread.sleep(1000);
} else {
System.out.println("consumer:队列空了,换个满的");
current = exchanger.exchange(current);
System.out.println("consumer:换满的成功~~~~~~~~~~~~~~~~~~~~~~");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
应用场景总结
Exchanger 可以用于各种应用场景,具体取决于具体的 Exchanger 实现。常见的场景包括:
- 数据交换
- 在多线程环境中,两个线程可以通过 Exchanger 进行数据交换。
- 数据采集
- 在数据采集系统中,可以使用 Exchanger 在采集线程和处理线程间进行数据交换。