采用ReentrantLock+Condition控制异步转同步流程。或者叫"保护性暂停 - Guarded Suspension 模式"
具体思路见下面代码及注释
/**
* 采用ReentrantLock+Condition控制异步转同步流程
*
* @author banshan
* @since 2023/12/14
*/
public class Async2SyncLock {
// 同一个ReentrantLock锁
private static Lock lock = new ReentrantLock();
/**
* 缓存condition Map集,异步唤醒时从map中获取
*/
private static Map<String, Condition> conditionMap = new HashMap<>();
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
new Thread(() -> process()).start();
// 随机间隔500ms内模拟并发
int random = new Random().nextInt(500);
Thread.sleep(random);
}
}
/**
* 异步转同步处理
*/
private static void process() {
lock.lock();
String id = UUID.randomUUID().toString();
// 为每个线程分配独立的condition
Condition condition = lock.newCondition();
System.out.println(id + "-await begin: " + stampToDate(System.currentTimeMillis()));
// 订阅
subscribe(id, condition);
// 业务处理
businessProcess(id);
// Note: signaler线程用来模拟businessProcess业务处理流程完成后的异步通知
new Thread(new Signaler(id)).start();
try {
// 阻塞线程 10s结束线程
condition.await(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(id + "-await end: " + stampToDate(System.currentTimeMillis()));
lock.unlock();
}
/**
* 订阅
*
* @param id
* @param condition
*/
private static void subscribe(String id, Condition condition) {
conditionMap.put(id, condition);
}
/**
* 取消订阅
*
* @param id
*/
private static Condition unsubscribe(String id) {
return conditionMap.remove(id);
}
/**
* 唤醒线程
*/
private static class Signaler extends Thread {
private String id;
public Signaler(String id) {
this.id = id;
}
@Override
public void run() {
lock.lock();
// 休眠3s模拟异步处理完成后再进行唤醒
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(id + "-signaler start: " + stampToDate(System.currentTimeMillis()));
Condition condition = unsubscribe(id);
// 唤醒线程
condition.signal();
lock.unlock();
}
}
/**
* 业务处理 (异步)
*
* @param id
*/
private static void businessProcess(String id) {
// 异步处理,提高吞吐量
}
/**
* 时间戳转日期
*
* @param stamp
* @return
*/
private static String stampToDate(long stamp) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date(stamp);
return simpleDateFormat.format(date);
}
}