异步与同步的核心区别
- 同步调用:调用方阻塞等待结果返回
- 异步调用:调用方立即返回,通过回调/轮询等方式获取结果
本文重点讨论如何将异步调用转为同步阻塞模式,以下是五种实现方案:
方法一:使用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异步调用转同步的资料请关注代码网其它相关文章!
发表评论