多线程编程:线程通信¶
在多线程编程中,线程之间的通信是一个非常重要的概念。线程通信允许线程之间共享数据、协调任务以及同步操作。Java提供了多种机制来实现线程通信,包括wait()
、notify()
、notifyAll()
方法以及BlockingQueue
等工具类。本文将详细介绍这些机制,并通过代码示例帮助你理解它们的使用方法。
1. 线程通信的基本概念¶
在多线程环境中,线程之间可能需要共享数据或协调任务。为了实现这一点,线程之间需要进行通信。Java提供了以下几种主要的线程通信机制:
wait()
和notify()
方法:这些方法用于在对象监视器上进行线程等待和唤醒操作。BlockingQueue
:这是一个线程安全的队列,支持阻塞操作,常用于生产者-消费者模型。Condition
接口:与Lock
配合使用,提供了更灵活的线程通信机制。
2. 使用 wait()
和 notify()
进行线程通信¶
wait()
和 notify()
是Object
类中的方法,用于在对象监视器上进行线程通信。wait()
方法使当前线程进入等待状态,直到其他线程调用notify()
或notifyAll()
方法唤醒它。
2.1 代码示例:生产者-消费者模型¶
以下是一个简单的生产者-消费者模型的示例,使用wait()
和notify()
方法实现线程通信。
class SharedResource {
private int data;
private boolean available = false;
public synchronized void produce(int value) {
while (available) {
try {
wait(); // 等待消费者消费
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
data = value;
available = true;
System.out.println("Produced: " + data);
notify(); // 通知消费者可以消费了
}
public synchronized int consume() {
while (!available) {
try {
wait(); // 等待生产者生产
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
available = false;
System.out.println("Consumed: " + data);
notify(); // 通知生产者可以生产了
return data;
}
}
class Producer extends Thread {
private SharedResource resource;
public Producer(SharedResource resource) {
this.resource = resource;
}
public void run() {
for (int i = 0; i < 5; i++) {
resource.produce(i);
}
}
}
class Consumer extends Thread {
private SharedResource resource;
public Consumer(SharedResource resource) {
this.resource = resource;
}
public void run() {
for (int i = 0; i < 5; i++) {
resource.consume();
}
}
}
public class ProducerConsumerExample {
public static void main(String[] args) {
SharedResource resource = new SharedResource();
Producer producer = new Producer(resource);
Consumer consumer = new Consumer(resource);
producer.start();
consumer.start();
}
}
2.2 代码解释¶
SharedResource
类:这是一个共享资源类,包含produce()
和consume()
方法。produce()
方法用于生产数据,consume()
方法用于消费数据。wait()
方法:当资源不可用时,线程调用wait()
方法进入等待状态。notify()
方法:当资源可用时,线程调用notify()
方法唤醒等待的线程。
3. 使用 BlockingQueue
进行线程通信¶
BlockingQueue
是一个线程安全的队列,支持阻塞操作。它常用于生产者-消费者模型,简化了线程通信的实现。
3.1 代码示例:使用 BlockingQueue
实现生产者-消费者模型¶
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
class Producer implements Runnable {
private final BlockingQueue<Integer> queue;
public Producer(BlockingQueue<Integer> queue) {
this.queue = queue;
}
public void run() {
try {
for (int i = 0; i < 5; i++) {
queue.put(i); // 生产数据并放入队列
System.out.println("Produced: " + i);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
class Consumer implements Runnable {
private final BlockingQueue<Integer> queue;
public Consumer(BlockingQueue<Integer> queue) {
this.queue = queue;
}
public void run() {
try {
for (int i = 0; i < 5; i++) {
int value = queue.take(); // 从队列中消费数据
System.out.println("Consumed: " + value);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
public class BlockingQueueExample {
public static void main(String[] args) {
BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(5);
Thread producer = new Thread(new Producer(queue));
Thread consumer = new Thread(new Consumer(queue));
producer.start();
consumer.start();
}
}
3.2 代码解释¶
BlockingQueue
:这是一个线程安全的队列,支持阻塞操作。put()
方法在队列满时会阻塞,take()
方法在队列空时会阻塞。- 生产者:生产者线程调用
put()
方法将数据放入队列。 - 消费者:消费者线程调用
take()
方法从队列中取出数据。
4. 使用 Condition
进行线程通信¶
Condition
接口与Lock
配合使用,提供了更灵活的线程通信机制。Condition
允许线程在某些条件下等待,并在条件满足时被唤醒。
4.1 代码示例:使用 Condition
实现生产者-消费者模型¶
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class SharedResource {
private int data;
private boolean available = false;
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
public void produce(int value) {
lock.lock();
try {
while (available) {
condition.await(); // 等待消费者消费
}
data = value;
available = true;
System.out.println("Produced: " + data);
condition.signal(); // 通知消费者可以消费了
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
lock.unlock();
}
}
public int consume() {
lock.lock();
try {
while (!available) {
condition.await(); // 等待生产者生产
}
available = false;
System.out.println("Consumed: " + data);
condition.signal(); // 通知生产者可以生产了
return data;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return -1;
} finally {
lock.unlock();
}
}
}
class Producer extends Thread {
private SharedResource resource;
public Producer(SharedResource resource) {
this.resource = resource;
}
public void run() {
for (int i = 0; i < 5; i++) {
resource.produce(i);
}
}
}
class Consumer extends Thread {
private SharedResource resource;
public Consumer(SharedResource resource) {
this.resource = resource;
}
public void run() {
for (int i = 0; i < 5; i++) {
resource.consume();
}
}
}
public class ConditionExample {
public static void main(String[] args) {
SharedResource resource = new SharedResource();
Producer producer = new Producer(resource);
Consumer consumer = new Consumer(resource);
producer.start();
consumer.start();
}
}
4.2 代码解释¶
Lock
和Condition
:Lock
用于控制对共享资源的访问,Condition
用于线程之间的通信。await()
方法:使当前线程等待,直到其他线程调用signal()
或signalAll()
方法。signal()
方法:唤醒等待的线程。
5. 练习题¶
5.1 简单练习¶
- 修改生产者-消费者模型的代码,使得生产者生产10个数据,消费者消费10个数据。
5.2 中等练习¶
- 使用
BlockingQueue
实现一个多生产者-多消费者的模型,确保每个生产者生产的数据都能被消费者正确消费。
5.3 复杂练习¶
- 使用
Condition
实现一个读写锁模型,允许多个读线程同时访问共享资源,但写线程独占访问。
6. 总结¶
- 线程通信是多线程编程中的重要概念,用于线程之间的数据共享和任务协调。
wait()
和notify()
是基本的线程通信机制,适用于简单的生产者-消费者模型。BlockingQueue
提供了线程安全的队列,简化了生产者-消费者模型的实现。Condition
与Lock
配合使用,提供了更灵活的线程通信机制。
通过掌握这些线程通信机制,你可以编写出高效、可靠的多线程程序。