Java中线程间的通信机制主要用于协调多个线程之间的协作,使得它们能够在特定的时机交换信息、同步执行或触发特定行为。
以下是Java中常用的线程间通信机制:
- 共享变量与同步机制:
- volatile关键字:用来修饰共享变量,确保线程对变量的修改对其他线程立即可见,消除指令重排序带来的影响,适用于简单的状态标记等场景。
- synchronized关键字:用于实现临界区同步,通过锁定特定对象,确保同一时刻只有一个线程访问共享资源。在synchronized代码块或方法中,可以安全地修改共享变量,同时配合wait()、notify()、notifyAll()方法实现线程间的通信。
- wait():当前线程释放锁并进入等待状态,直到被其他线程调用notify()或notifyAll()唤醒。
- notify():唤醒一个因调用wait()而处于等待状态的线程。
- notifyAll():唤醒所有因调用wait()而处于等待状态的线程。
/**
* 共享变量与同步机制(synchronized + wait/notify)
*/
public class SharedVarExample {
private int value;
private final Object lock = new Object();
public void increment() {
synchronized (lock) {
value++;
System.out.printf("增加value值为:%d\n", value);
lock.notify();//唤醒等待的线程
}
}
public void decrement() {
synchronized (lock) {
while (value == 0) { //条件不满足时等待
try {
System.out.println("等待value值被修改...");
lock.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
value--;
System.out.printf("减少value值为:%d\n", value);
}
}
public static void main(String[] args) throws InterruptedException {
SharedVarExample example = new SharedVarExample();
//创建修改共享资源的线程
Thread updateThread = new Thread(new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(1); //模拟延迟,确保等待线程先启动
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
example.increment();
}
});
//创建等待共享资源变化的线程
Thread waitThread = new Thread(new Runnable() {
@Override
public void run() {
example.decrement();
}
});
//启动线程
waitThread.start();
updateThread.start();
//等待两个线程结束
waitThread.join();
updateThread.join();
System.out.println("两个线程都执行完了。");
}
//执行结果
/*
等待value值被修改...
增加value值为:1
减少value值为:0
两个线程都执行完了。
*/
}
- Lock与Condition接口:
- ReentrantLock:可重入锁,比synchronized提供了更灵活的锁定机制,如公平锁、非公平锁的选择,以及尝试加锁、定时加锁等操作。
- Condition:与ReentrantLock配套使用,替代了synchronized的wait/notify机制。每个Condition对象都关联一个Lock,线程可以调用condition.await()进入等待状态,并在其他线程调用condition.signal()或condition.signalAll()时被唤醒。Condition允许创建多个等待-通知条件,适用于更为复杂的同步场景。
/**
* Lock与Condition接口
*/
public class LockConditionExample {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private boolean ready;
public void before() {
lock.lock();
try {
// 执行一些操作...
ready = true;
condition.signalAll(); // 唤醒等待的线程
} finally {
lock.unlock();
}
}
public void after() {
lock.lock();
try {
while (!ready) { // 条件不满足时等待
condition.await();
}
// 执行后续操作...
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}
}
- 阻塞队列(BlockingQueue):
- Java提供了多种实现如ArrayBlockingQueue、LinkedBlockingQueue等,它们在插入元素(生产者)和移除元素(消费者)时可以自动阻塞和唤醒线程,实现了生产者与消费者模式的线程间通信。线程无需显式调用wait/notify,只需进行正常的队列操作即可实现线程间的协调。
public class BlockingQueueExample {
private static final int QUEUE_CAPACITY = 10;
private final BlockingQueue<String> queue = new ArrayBlockingQueue<>(QUEUE_CAPACITY);
class ProducerThread extends Thread {
@Override
public void run() {
String item = "Item";
try {
while (true) {
TimeUnit.MILLISECONDS.sleep(100); //模拟延迟
queue.put(item);
System.out.println("Produced: " + item);
item += " (new)";
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
class ConsumerThread extends Thread {
@Override
public void run() {
try {
while (true) {
TimeUnit.MILLISECONDS.sleep(100); //模拟延迟
String item = queue.take();
System.out.println("Consumed: " + item);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
public static void main(String[] args) {
BlockingQueueExample example = new BlockingQueueExample();
ProducerThread producer = example.new ProducerThread();
ConsumerThread consumer = example.new ConsumerThread();
// 启动生产者和消费者线程
producer.start();
consumer.start();
// 为了让示例程序在控制台输出一些信息后退出,这里添加了主线程睡眠一段时间
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 为了演示目的,这里强制中断生产者和消费者线程(实际应用中可能需要更优雅的方式停止线程)
producer.interrupt();
consumer.interrupt();
}
//执行结果:
/*
Produced: Item
Consumed: Item
Produced: Item (new)
Consumed: Item (new)
Produced: Item (new) (new)
Consumed: Item (new) (new)
Consumed: Item (new) (new) (new)
Produced: Item (new) (new) (new)
......
*/
}
- 管道流(PipedInputStream/PipedOutputStream、PipedReader/PipedWriter):
- 管道流允许在两个线程之间直接传递数据,一个线程通过输出流写入数据,另一个线程通过输入流读取数据。这种方式适用于线程间需要交换大量数据或流式数据的情况。
public class PipedStreamExample {
private final PipedInputStream input = new PipedInputStream();
private final PipedOutputStream output = new PipedOutputStream(input);
public PipedStreamExample() throws IOException {
}
class ReaderThread extends Thread {
@Override
public void run() {
try {
byte[] buffer = new byte[1024];
int read;
while ((read = input.read(buffer)) != -1) {
System.out.println("Received: " + new String(buffer, 0, read));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class WriterThread extends Thread {
@Override
public void run() {
try {
String message = "Hello, Pipe!";
output.write(message.getBytes());
output.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws IOException {
PipedStreamExample example = new PipedStreamExample();
// 创建并启动读取线程
ReaderThread reader = example.new ReaderThread();
reader.start();
// 确保读取线程已启动后再启动写入线程
try {
while (!reader.isAlive()) {
TimeUnit.MILLISECONDS.sleep(100);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
WriterThread writer = example.new WriterThread();
writer.start();
// 等待写入线程完成(实际应用中可能需要更优雅的方式停止线程)
try {
writer.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
//执行结果:
/*
Received: Hello, Pipe!
*/
}
- Future与Callable:
- 通过ExecutorService提交Callable任务并获取Future对象,主线程可以调用Future的get()方法阻塞等待异步任务的结果,从而实现线程间的同步通信。
public class FutureCallableExample {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(new Callable<Integer>() {
@Override
public Integer call() {
return longRunningTask();
}
});
// 主线程可以继续执行其他任务...
Integer result = future.get(); // 阻塞等待结果
System.out.println("Result: " + result);
executor.shutdown();
}
private static int longRunningTask() {
// 执行耗时操作...
return 666;
}
//执行结果:
/*
Result: 666
*/
}
- 并发工具类:
- CountDownLatch:允许一个或多个线程等待其他线程完成一组操作后再继续执行,常用于实现线程间的阶段性同步。
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(3);
for (int i = 0; i < 3; ++i) { // 3个任务
new Thread(new Worker(startSignal, doneSignal)).start();
}
doSomethingElse(); // 在所有任务开始前做其他事
startSignal.countDown(); // 放行所有等待的任务
doneSignal.await(); // 等待所有任务完成
System.out.println("所有任务执行完成");
}
private static void doSomethingElse() {
System.out.println("在所有任务开始前做其他事");
// ...
}
static class Worker implements Runnable {
private final CountDownLatch startSignal;
private final CountDownLatch doneSignal;
Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
this.startSignal = startSignal;
this.doneSignal = doneSignal;
}
@Override
public void run() {
try {
startSignal.await(); // 等待开始信号
doWork(); // 执行任务
doneSignal.countDown(); // 任务完成,计数减一
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
private void doWork() {
System.out.println("执行任务...");
// ...
}
}
//执行结果:
/*
在所有任务开始前做其他事
执行任务...
执行任务...
执行任务...
所有任务执行完成
*/
}
- CyclicBarrier:让一组线程到达某个屏障(同步点)时暂停,直到所有线程都到达后才解除阻塞,继续执行。
public class CyclicBarrierExample {
public static void main(String[] args) {
int parties = 4;
CyclicBarrier barrier = new CyclicBarrier(parties, () -> {
System.out.println("所有线程都已到达屏障");
});
for (int i = 0; i < parties; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " 线程执行中...");
barrier.await(); // 等待其他线程到达
System.out.println(Thread.currentThread().getName() + " 已达到障碍");
} catch (BrokenBarrierException | InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
//执行结果:
/*
Thread-0 线程执行中...
Thread-1 线程执行中...
Thread-2 线程执行中...
Thread-3 线程执行中...
所有线程都已到达屏障
Thread-3 已达到障碍
Thread-0 已达到障碍
Thread-2 已达到障碍
Thread-1 已达到障碍
*/
}
- Semaphore:基于计数的信号量,用于控制同时访问特定资源的线程数量,线程可以通过acquire()获取许可,release()释放许可,实现资源的协调与通信。
public class SemaphoreExample {
private final Semaphore semaphore = new Semaphore(3); // 最大并发数为3
public void accessResource() {
try {
semaphore.acquire(); // 获取许可,若无可用许可则阻塞
// 访问资源
System.out.println(Thread.currentThread().getName() + "线程正在访问资源");
Thread.sleep(1000); // 模拟耗时操作
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
semaphore.release(); // 释放许可
}
}
public static void main(String[] args) {
SemaphoreExample example = new SemaphoreExample();
for (int i = 0; i < 10; i++) {
new Thread(() -> example.accessResource()).start();
}
}
//执行结果:
/*
Thread-0线程正在访问资源
Thread-2线程正在访问资源
Thread-1线程正在访问资源
Thread-3线程正在访问资源
Thread-5线程正在访问资源
Thread-4线程正在访问资源
Thread-6线程正在访问资源
Thread-8线程正在访问资源
Thread-7线程正在访问资源
Thread-9线程正在访问资源
*/
}
在使用以上线程间通信机制时,需注意线程安全问题,避免数据竞争和死锁的发生。合理选择和组合这些机制,可以帮助编写出高效、可靠的多线程应用程序。