Java 中线程之间如何进行通信?
在 Java 中,线程间通信主要通过共享内存和线程同步机制实现。以下是核心方法及代码示例:
一、基础通信机制
1.使用wait()/notify()/notifyAll()
这是最经典的线程通信方式,需在synchronized块中使用:
// 共享资源类classMessage{privateStringcontent;privatebooleanempty=true;publicsynchronizedStringread(){while(empty){// 循环检查条件,防止虚假唤醒try{wait();// 释放锁,等待写入}catch(InterruptedExceptione){Thread.currentThread().interrupt();}}empty=true;notifyAll();// 通知所有等待线程returncontent;}publicsynchronizedvoidwrite(Stringmessage){while(!empty){// 等待读取完成try{wait();}catch(InterruptedExceptione){Thread.currentThread().interrupt();}}empty=false;this.content=message;notifyAll();// 通知等待的读取线程}}// 测试代码publicclassWaitNotifyDemo{publicstaticvoidmain(String[]args){Messagemessage=newMessage();// 写入线程Threadwriter=newThread(()->{String[]messages={"消息1","消息2","消息3"};for(Stringmsg:messages){message.write(msg);System.out.println("写入: "+msg);}});// 读取线程Threadreader=newThread(()->{for(inti=0;i<3;i++){Stringmsg=message.read();System.out.println("读取: "+msg);}});reader.start();writer.start();}}2.使用BlockingQueue(推荐)
Java 并发包提供了现成的线程安全队列:
importjava.util.concurrent.BlockingQueue;importjava.util.concurrent.LinkedBlockingQueue;publicclassBlockingQueueDemo{publicstaticvoidmain(String[]args){// 创建容量为10的阻塞队列BlockingQueue<String>queue=newLinkedBlockingQueue<>(10);// 生产者线程Threadproducer=newThread(()->{try{for(inti=1;i<=5;i++){Stringmessage="消息-"+i;queue.put(message);// 队列满时自动阻塞System.out.println("生产: "+message);Thread.sleep(1000);}queue.put("END");// 结束标志}catch(InterruptedExceptione){Thread.currentThread().interrupt();}});// 消费者线程Threadconsumer=newThread(()->{try{Stringmessage;while(!(message=queue.take()).equals("END")){// 队列空时自动阻塞System.out.println("消费: "+message);}System.out.println("消费者结束");}catch(InterruptedExceptione){Thread.currentThread().interrupt();}});producer.start();consumer.start();}}二、高级通信工具
3.使用CountDownLatch
适用于一个线程等待多个线程完成:
importjava.util.concurrent.CountDownLatch;publicclassCountDownLatchDemo{publicstaticvoidmain(String[]args)throwsInterruptedException{intworkerCount=3;CountDownLatchstartSignal=newCountDownLatch(1);// 开始信号CountDownLatchdoneSignal=newCountDownLatch(workerCount);// 完成信号for(inti=0;i<workerCount;i++){newThread(newWorker(startSignal,doneSignal),"工人-"+i).start();}System.out.println("主线程: 准备工作完成,通知工人开始工作");startSignal.countDown();// 发出开始信号doneSignal.await();// 等待所有工人完成System.out.println("主线程: 所有工人工作完成");}staticclassWorkerimplementsRunnable{privatefinalCountDownLatchstartSignal;privatefinalCountDownLatchdoneSignal;Worker(CountDownLatchstartSignal,CountDownLatchdoneSignal){this.startSignal=startSignal;this.doneSignal=doneSignal;}@Overridepublicvoidrun(){try{startSignal.await();// 等待开始信号System.out.println(Thread.currentThread().getName()+" 正在工作...");Thread.sleep(2000);// 模拟工作时间System.out.println(Thread.currentThread().getName()+" 工作完成");doneSignal.countDown();// 通知完成}catch(InterruptedExceptione){Thread.currentThread().interrupt();}}}}4.使用CyclicBarrier
适用于多个线程相互等待:
importjava.util.concurrent.BrokenBarrierException;importjava.util.concurrent.CyclicBarrier;publicclassCyclicBarrierDemo{publicstaticvoidmain(String[]args){intthreadCount=3;CyclicBarrierbarrier=newCyclicBarrier(threadCount,()->{System.out.println("所有线程已就位,开始下一阶段");});for(inti=0;i<threadCount;i++){newThread(newTask(barrier),"线程-"+i).start();}}staticclassTaskimplementsRunnable{privatefinalCyclicBarrierbarrier;Task(CyclicBarrierbarrier){this.barrier=barrier;}@Overridepublicvoidrun(){try{System.out.println(Thread.currentThread().getName()+" 完成阶段1");barrier.await();// 等待其他线程System.out.println(Thread.currentThread().getName()+" 完成阶段2");barrier.await();System.out.println(Thread.currentThread().getName()+" 全部完成");}catch(InterruptedException|BrokenBarrierExceptione){Thread.currentThread().interrupt();}}}}三、选择建议
| 场景 | 推荐方案 | 优点 |
|---|---|---|
| 简单的生产消费模式 | BlockingQueue | 无需手动同步,代码简洁 |
| 复杂条件等待 | wait()/notify() | 灵活性高,可精确控制 |
| 主线程等待子线程 | CountDownLatch | 一次性使用,简单直观 |
| 多线程协同工作 | CyclicBarrier | 可重复使用,适合分阶段任务 |
注意事项
- 避免死锁:确保锁的获取顺序一致
- 防止虚假唤醒:始终在循环中检查条件
- 优先使用高层工具:如
BlockingQueue比手动wait/notify更安全
根据具体需求选择合适的通信机制,可以显著提高多线程程序的可靠性和性能。
