异步与同步的核心区别
- 同步调用:调用方阻塞等待结果返回
- 异步调用:调用方立即返回,通过回调/轮询等方式获取结果
本文重点讨论如何将异步调用转为同步阻塞模式,以下是五种实现方案:
方法一:使用wait/notify + synchronized
代码示例
public class producerconsumerexample {
private static final int buffer_size = 5;
private final object lock = new object();
private int[] buffer = new int[buffer_size];
private int count = 0;
// 生产者线程
public void produce() throws interruptedexception {
int value = 0;
while (true) {
synchronized (lock) {
while (count == buffer_size) {
system.out.println("缓冲区已满,生产者等待...");
lock.wait();
}
buffer[count++] = value++;
system.out.println("生产数据: " + value + ",缓冲区数量: " + count);
lock.notify();
}
thread.sleep(1000);
}
}
// 消费者线程
public void consume() throws interruptedexception {
while (true) {
synchronized (lock) {
while (count == 0) {
system.out.println("缓冲区为空,消费者等待...");
lock.wait();
}
int value = buffer[--count];
system.out.println("消费数据: " + value + ",缓冲区数量: " + count);
lock.notify();
}
thread.sleep(1500);
}
}
public static void main(string[] args) {
producerconsumerexample example = new producerconsumerexample();
// 启动生产者和消费者线程
new thread(example::produce).start();
new thread(example::consume).start();
}
}
关键要点
共享资源保护:通过synchronized(lock)保证线程安全
条件判断:
- while循环而非if防止虚假唤醒
- 缓冲区满时生产者等待(wait())
- 缓冲区空时消费者等待(wait())
协作机制:每次操作后通过notify()唤醒等待线程
方法对比:
- notify():唤醒单个等待线程
- notifyall():唤醒所有等待线程(适用于多生产者场景)
方法二:使用reentrantlock + condition
代码示例
import java.util.concurrent.locks.condition;
import java.util.concurrent.locks.reentrantlock;
public class testreentrantlock4 {
static reentrantlock lock = new reentrantlock();
static condition moneycondition = lock.newcondition();
static condition ticketcondition = lock.newcondition();
static boolean havemoney = false;
static boolean haveticket = false;
public static void main(string[] args) throws interruptedexception {
// 农民1(等钱)
new thread(() -> {
lock.lock();
try {
while (!havemoney) {
system.out.println("农民1等待资金...");
moneycondition.await();
}
system.out.println("农民1获得资金,回家!");
} finally {
lock.unlock();
}
}, "farmer1").start();
// 农民2(等票)
new thread(() -> {
lock.lock();
try {
while (!haveticket) {
system.out.println("农民2等待车票...");
ticketcondition.await();
}
system.out.println("农民2获得车票,回家!");
} finally {
lock.unlock();
}
}, "farmer2").start();
// 主线程模拟发放条件
thread.sleep(1000);
lock.lock();
try {
havemoney = true;
moneycondition.signal();
system.out.println("资金已发放!");
haveticket = true;
ticketcondition.signal();
system.out.println("车票已发放!");
} finally {
lock.unlock();
}
}
}
核心特性
多条件支持:
- 一个锁对象可绑定多个condition(如moneycondition/ticketcondition)
精准唤醒:
- await():释放锁并等待特定条件
- signal():唤醒满足条件的等待线程
代码结构:
- 必须在lock.lock()和finally unlock()之间操作
- 条件判断使用while循环防止虚假唤醒
方法三:future(callable + executorservice)
代码示例
import java.util.concurrent.*;
public class futureexample {
public static void main(string[] args) {
executorservice executor = executors.newsinglethreadexecutor();
future<integer> future = executor.submit(() -> {
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
thread.sleep(10);
}
return sum;
});
system.out.println("主线程执行其他任务...");
try {
integer result = future.get(2, timeunit.seconds);
system.out.println("计算结果: 1+2+...+100 = " + result);
} catch (timeoutexception e) {
system.err.println("计算超时!");
future.cancel(true);
} catch (exception e) {
e.printstacktrace();
} finally {
executor.shutdown();
}
}
}
关键api
| 方法 | 作用 |
|---|---|
| future.get() | 阻塞获取结果(可设置超时) |
| future.cancel() | 取消任务执行 |
| isdone() | 检查任务是否完成 |
执行流程
- 提交callable任务到线程池
- 主线程继续执行其他操作
- 调用future.get()阻塞等待结果
- 处理可能出现的异常情况
- 最终关闭线程池资源
方法四:countdownlatch(多线程同步)
代码示例
import java.util.concurrent.countdownlatch;
import java.util.concurrent.executorservice;
import java.util.concurrent.timeunit;
public class countdownlatchexample {
private static final int runners = 5;
private static final countdownlatch startsignal = new countdownlatch(1);
private static final countdownlatch readysignal = new countdownlatch(runners);
public static void main(string[] args) throws interruptedexception {
executorservice executor = executors.newfixedthreadpool(runners);
for (int i = 1; i <= runners; i++) {
executor.execute(() -> {
try {
system.out.println("运动员" + i + "正在准备...");
timeunit.milliseconds.sleep(300);
readysignal.countdown();
startsignal.await();
system.out.println("运动员" + i + "起跑!");
timeunit.milliseconds.sleep((long)(math.random() * 1000));
system.out.println("运动员" + i + "到达终点!");
} catch (interruptedexception e) {
e.printstacktrace();
}
});
}
system.out.println("裁判等待运动员就位...");
readysignal.await();
system.out.println("\n所有运动员就位!");
timeunit.seconds.sleep(1);
system.out.println("发令枪响!");
startsignal.countdown();
executor.shutdown();
executor.awaittermination(5, timeunit.seconds);
system.out.println("\n比赛结束!");
}
}
应用场景
- 多线程初始化后统一执行:如服务启动时等待所有组件就绪
- 并发测试控制:模拟固定数量请求同时发起
- 事件驱动编程:等待多个前置条件完成
方法五:cyclicbarrier(可重用同步屏障)
代码示例
import java.util.concurrent.brokenbarrierexception;
import java.util.concurrent.cyclicbarrier;
public class cyclicbarrierexample {
private static final cyclicbarrier barrier =
new cyclicbarrier(3, () -> system.out.println("\n===== 进入下一阶段 ====="));
public static void main(string[] args) {
for (int i = 1; i <= 3; i++) {
new thread(new teammember(i)).start();
}
}
static class teammember implements runnable {
private int id;
public teammember(int id) {
this.id = id;
}
@override
public void run() {
try {
dowork("需求分析", 1000);
barrier.await();
dowork("开发编码", 1500);
barrier.await();
dowork("测试部署", 800);
barrier.await();
} catch (exception e) {
e.printstacktrace();
}
}
private void dowork(string phase, int basetime) throws interruptedexception {
int time = basetime + (int)(math.random() * 500);
system.out.printf("%s 完成%s(%dms)\n",
thread.currentthread().getname(), phase, time);
thread.sleep(time);
}
}
}
核心特性
| 对比项 | countdownlatch | cyclicbarrier |
|---|---|---|
| 重用性 | 一次性使用 | 可重复触发 |
| 线程关系 | 主线程等待子线程 | 子线程相互等待 |
| 典型场景 | 线程初始化完成后执行 | 多阶段任务协作 |
总结对比表
| 方法 | 适用场景 | 核心机制 | 扩展性 |
|---|---|---|---|
| wait/notify | 简单生产者-消费者模型 | 对象锁的等待/通知机制 | 低 |
| reentrantlock+condition | 需要多个条件变量 | 精细条件控制 | 中 |
| future | 异步任务结果获取 | 任务提交与结果回调 | 高 |
| countdownlatch | 多线程等待单一事件 | 计数器递减触发机制 | 中 |
| cyclicbarrier | 多阶段任务同步 | 可重置的屏障计数机制 | 高 |
最佳实践建议:
- 简单同步场景优先使用countdownlatch
- 需要结果返回时使用future
- 多条件或多阶段场景推荐cyclicbarrier
- 避免使用过时的object.wait/notify直接控制
以上就是java中将异步调用转为同步的五种方法的详细内容,更多关于java异步调用转同步的资料请关注代码网其它相关文章!
发表评论