详细说明线程间如何进行通信。
参考回答
线程间通信可以通过多种方式实现,最常见的是使用共享对象和等待/通知机制,比如通过 wait()
、notify()
和 notifyAll()
方法进行协调。此外,还可以使用更高级的工具,比如 java.util.concurrent
包中的类,如 CountDownLatch
、CyclicBarrier
、Semaphore
和 BlockingQueue
。
一个简单的例子是使用共享对象作为锁,在线程之间共享数据并通过 wait()
和 notify()
进行通信。
详细讲解与拓展
线程间通信的核心在于线程协作,通常需要解决以下两个问题:
- 如何共享数据:多个线程之间需要有共同的操作对象。
- 如何协调执行顺序:线程之间需要知道何时进行操作或等待。
方法一:使用 wait()
和 notify()
wait()
和 notify()
是 Object
类的方法,必须在同步块中调用,否则会抛出 IllegalMonitorStateException
。这些方法可以通过共享对象来协调线程间的通信。
代码示例:生产者-消费者模型
class SharedResource {
private int data;
private boolean isProduced = false;
public synchronized void produce(int value) throws InterruptedException {
while (isProduced) {
wait(); // 等待消费者消费
}
data = value;
System.out.println("Produced: " + data);
isProduced = true;
notify(); // 通知消费者可以消费了
}
public synchronized int consume() throws InterruptedException {
while (!isProduced) {
wait(); // 等待生产者生产
}
System.out.println("Consumed: " + data);
isProduced = false;
notify(); // 通知生产者可以生产了
return data;
}
}
public class ThreadCommunication {
public static void main(String[] args) {
SharedResource resource = new SharedResource();
Thread producer = new Thread(() -> {
try {
for (int i = 0; i < 5; i++) {
resource.produce(i);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
Thread consumer = new Thread(() -> {
try {
for (int i = 0; i < 5; i++) {
resource.consume();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
producer.start();
consumer.start();
}
}
关键点解释:
wait()
:使当前线程等待,直到被其他线程通过notify()
唤醒。notify()
:唤醒一个正在等待的线程。notifyAll()
:唤醒所有正在等待的线程。
方法二:使用 java.util.concurrent
包中的工具类
为了简化线程间通信,Java 提供了许多并发工具类。
使用 BlockingQueue
BlockingQueue
是线程安全的队列,可以用来实现生产者-消费者模型,避免手动处理同步问题。
代码示例:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class BlockingQueueExample {
public static void main(String[] args) {
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(5);
Thread producer = new Thread(() -> {
try {
for (int i = 0; i < 5; i++) {
queue.put(i); // 队列满时自动阻塞
System.out.println("Produced: " + i);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
Thread consumer = new Thread(() -> {
try {
for (int i = 0; i < 5; i++) {
int value = queue.take(); // 队列为空时自动阻塞
System.out.println("Consumed: " + value);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
producer.start();
consumer.start();
}
}
优点:
BlockingQueue
自动处理了线程同步问题。- 简化了代码逻辑。
方法三:使用 CountDownLatch
CountDownLatch
用于一个或多个线程等待其他线程完成某些操作。
代码示例:
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3);
Runnable task = () -> {
System.out.println(Thread.currentThread().getName() + " completed task");
latch.countDown(); // 每完成一个任务,计数减1
};
new Thread(task).start();
new Thread(task).start();
new Thread(task).start();
latch.await(); // 等待所有任务完成
System.out.println("All tasks are completed!");
}
}
关键点解释:
countDown()
:将计数器减1。await()
:主线程阻塞,直到计数器为0。
拓展知识
- 线程安全性:线程间通信时需要注意共享资源的安全性,常用方法是加锁或使用并发工具类。
-
ReentrantLock 的条件变量:相比 synchronized,ReentrantLock提供了更灵活的线程通信方式,通过 Condition实现等待/通知机制。
Lock lock = new ReentrantLock(); Condition condition = lock.newCondition();
-
CompletableFuture
:适用于异步任务之间的通信,可以简化复杂的线程间依赖。