Java 线程间的通信方式

一、概述

在Java中,线程之间的通信主要涉及线程之间如何交换信息或协调行动。常见的线程通信方式有以下几种:

  1. 共享内存(通过共享对象进行通信)
  2. wait/notify机制
  3. Lock和Condition
  4. 使用阻塞队列(BlockingQueue)
  5. 使用管道(PipedInputStream/PipedOutputStream 或 PipedReader/PipedWriter)
  6. 使用信号量(Semaphore)等同步工具
  7. 使用CountDownLatch、CyclicBarrier等同步辅助类
  8. 使用Exchanger
  9. 使用Future和Callable
  10. 使用CompletableFuture

二、详细实现与示例

2.1 共享内存(共享变量)

线程之间通过读写共享的变量来进行通信。这是最常用的一种方式,但需要同步机制来保证数据的一致性(如synchronized、volatile、Lock等)。

java 复制代码
// 示例:使用volatile共享变量
public class SharedVariable {
    private volatile boolean flag = false;
    
    public void setFlag() {
        flag = true;
    }
    
    public void waitForFlag() {
        while (!flag) {
            // 等待
        }
        System.out.println("Flag is true!");
    }
}

2.2 wait()/notify()/notifyAll()机制

这是Object类提供的方法,用于线程间的等待和唤醒。必须搭配synchronized使用,因为wait/notify需要先获得对象的锁。

java 复制代码
public class WaitNotifyExample {
    private final Object lock = new Object();
    private boolean condition = false;
    
    // 等待方
    public void await() throws InterruptedException {
        synchronized (lock) {
            while (!condition) {
                lock.wait(); // 释放锁并等待
            }
            // 执行后续操作
            System.out.println("条件满足,继续执行");
        }
    }
    
    // 通知方
    public void signal() {
        synchronized (lock) {
            condition = true;
            lock.notify(); // 唤醒一个等待线程
            // lock.notifyAll(); // 唤醒所有等待线程
        }
    }
}

2.3 Lock和Condition(显式锁)

Lock提供了比synchronized更灵活的锁机制,而Condition则提供了线程间协调等待和唤醒的功能,类似于wait/notify,但可以更精细地控制。

java 复制代码
import java.util.concurrent.locks.*;

public class LockConditionExample {
    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();
    private boolean ready = false;
    
    public void await() throws InterruptedException {
        lock.lock();
        try {
            while (!ready) {
                condition.await(); // 等待
            }
            System.out.println("准备就绪,开始工作");
        } finally {
            lock.unlock();
        }
    }
    
    public void signal() {
        lock.lock();
        try {
            ready = true;
            condition.signal(); // 发送信号
        } finally {
            lock.unlock();
        }
    }
}

2.4 阻塞队列(BlockingQueue)

BlockingQueue是一个接口,它定义了线程安全的队列,支持阻塞的插入和移除操作。当队列满时,插入操作会被阻塞;当队列空时,移除操作会被阻塞。

java 复制代码
import java.util.concurrent.*;

public class BlockingQueueExample {
    private BlockingQueue<String> queue = new LinkedBlockingQueue<>(10);
    
    // 生产者
    class Producer implements Runnable {
        @Override
        public void run() {
            try {
                String item = "item-" + System.currentTimeMillis();
                queue.put(item); // 队列满时会阻塞
                System.out.println("生产: " + item);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
    
    // 消费者
    class Consumer implements Runnable {
        @Override
        public void run() {
            try {
                String item = queue.take(); // 队列空时会阻塞
                System.out.println("消费: " + item);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
}

2.5 信号量(Semaphore)

信号量用来控制同时访问某个资源的线程数量,也可以用于线程间通信,但更偏向于同步。通过acquire()和release()操作。

java 复制代码
import java.util.concurrent.Semaphore;

public class SemaphoreExample {
    private Semaphore semaphore = new Semaphore(0); // 初始许可为0
    
    public void waitSignal() throws InterruptedException {
        System.out.println("等待信号...");
        semaphore.acquire(); // 获取许可,没有许可时阻塞
        System.out.println("收到信号,继续执行");
    }
    
    public void sendSignal() {
        System.out.println("发送信号");
        semaphore.release(); // 释放许可
    }
}

2.6 CountDownLatch

CountDownLatch允许一个或多个线程等待其他线程完成操作;

java 复制代码
import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(3);
        
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + " 完成任务");
                latch.countDown(); // 计数器减1
            }).start();
        }
        
        latch.await(); // 等待计数器归零
        System.out.println("所有任务完成,继续主线程");
    }
}

2.7 CyclicBarrier

CyclicBarrier让一组线程相互等待,到达一个公共屏障点。

java 复制代码
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierExample {
    public static void main(String[] args) {
        CyclicBarrier barrier = new CyclicBarrier(3, () -> {
            System.out.println("所有线程到达屏障,执行屏障动作");
        });
        
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + " 到达屏障");
                    barrier.await(); // 等待其他线程
                    System.out.println(Thread.currentThread().getName() + " 继续执行");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

2.8 Exchanger

Exchanger用于两个线程之间交换数据。每个线程在exchange()方法处等待,直到两个线程都调用exchange()方法,然后交换数据。

java 复制代码
import java.util.concurrent.Exchanger;

public class ExchangerExample {
    public static void main(String[] args) {
        Exchanger<String> exchanger = new Exchanger<>();
        
        new Thread(() -> {
            try {
                String data = "Thread1的数据";
                System.out.println("Thread1发送: " + data);
                String received = exchanger.exchange(data);
                System.out.println("Thread1收到: " + received);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
        
        new Thread(() -> {
            try {
                String data = "Thread2的数据";
                System.out.println("Thread2发送: " + data);
                String received = exchanger.exchange(data);
                System.out.println("Thread2收到: " + received);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

2.9 管道(Piped Streams)

管道是一种基于流的通信方式,一个线程向管道输出流写数据,另一个线程从管道输入流读数据。这种方式适用于线程间传输数据,但不如阻塞队列常用。

java 复制代码
import java.io.*;

public class PipedStreamExample {
    public static void main(String[] args) throws IOException {
        PipedOutputStream pos = new PipedOutputStream();
        PipedInputStream pis = new PipedInputStream(pos);
        
        // 写入线程
        new Thread(() -> {
            try {
                pos.write("Hello from pipe!".getBytes());
                pos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
        
        // 读取线程
        new Thread(() -> {
            try {
                int data;
                while ((data = pis.read()) != -1) {
                    System.out.print((char) data);
                }
                pis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
    }
}

2.10 Future/Callable

通过ExecutorService提交一个Callable任务,返回一个Future对象。主线程可以通过Future.get()方法获取子线程的执行结果,这实际上也是一种线程间通信(获取结果)。

java 复制代码
import java.util.concurrent.*;

public class FutureExample {
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        
        Callable<String> task = () -> {
            Thread.sleep(1000);
            return "任务结果";
        };
        
        Future<String> future = executor.submit(task);
        
        System.out.println("等待结果...");
        String result = future.get(); // 阻塞等待结果
        System.out.println("结果: " + result);
        
        executor.shutdown();
    }
}

2.11 CompletableFuture(Java 8+)

更强大的异步编程和线程通信。

java 复制代码
import java.util.concurrent.CompletableFuture;

public class CompletableFutureExample {
    public static void main(String[] args) {
        // 链式调用和组合
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello")
                .thenApply(s -> s + " World")
                .thenApply(String::toUpperCase);
        
        future.thenAccept(System.out::println); // 结果: HELLO WORLD
    }
}

三、选择建议

  1. 简单同步:使用synchronized + wait/notify
  2. 复杂同步:使用Lock + Condition
  3. 生产者-消费者:优先选择BlockingQueue
  4. 线程计数/等待:使用CountDownLatch
  5. 线程集合点:使用CyclicBarrier
  6. 数据交换:使用Exchanger
  7. 资源控制:使用Semaphore
  8. 异步结果:使用Future/CompletableFuture

.

相关推荐
不过如此195113 小时前
Jira系统中JQL语句的介绍
python·sql·jira
SimonKing13 小时前
神了,WebSocket竟然可以这么设计!
java·后端·程序员
allione13 小时前
Java设计模式-工厂模式
java·开发语言·设计模式
WKP941813 小时前
POI操作excel示例
java·开发语言·excel
xiaomin-Michael13 小时前
HTTP 错误码
java
万岳科技系统开发13 小时前
开源知识付费源码:实现在线课程系统与会员管理
开发语言·小程序
suncentwl13 小时前
完整开源答题pk小程序软件成品源码:对战、排名、题库一键部署
java·答题源码·答题pk软件
funcdefmain13 小时前
lsposed开发hook找不到类
java·android-studio
ejjdhdjdjdjdjjsl13 小时前
C#控件事件与数据存储实战
开发语言·c#
不过如此195113 小时前
Python操作Jira实现不同项目之间的Issue同步
python·jira·issue