管程
运用信号量操作可能过于复杂,不利于扩大生产,故而有了管程,可以更加轻松的实现同步和互斥操作。
在Java中,synchronized 关键字和条件变量(通过 Object 类中的 wait(), notify() 和 notifyAll() 方法实现)的组合可以有效地解决同步问题,如经典的生产者-消费者问题。以下是一个使用 synchronized 和条件变量来解决生产者和消费者问题的示例:
生产者和消费者问题
假设有一个共享的缓冲区,生产者向其中添加数据,消费者从中取数据。我们将使用一个固定大小的数组作为缓冲区。生产者在缓冲区满时需要等待,消费者在缓冲区空时需要等待。
public class ProducerConsumerExample {
private static final int CAPACITY = 5;
private final int[] buffer = new int[CAPACITY];
private int front = 0, rear = 0, count = 0;
public synchronized void produce(int value) throws InterruptedException {
while (count == CAPACITY) {
// 缓冲区满,等待消费者消费
wait();
}
buffer[rear] = value;
rear = (rear + 1) % CAPACITY;
count++;
System.out.println("Produced: " + value);
// 通知可能在等待的消费者
notifyAll();
}
public synchronized int consume() throws InterruptedException {
while (count == 0) {
// 缓冲区空,等待生产者生产
wait();
}
int value = buffer[front];
front = (front + 1) % CAPACITY;
count--;
System.out.println("Consumed: " + value);
// 通知可能在等待的生产者
notifyAll();
return value;
}
public static void main(String[] args) {
ProducerConsumerExample example = new ProducerConsumerExample();
// 创建生产者线程
Thread producerThread = new Thread(() -> {
try {
int value = 0;
while (true) {
example.produce(value++);
Thread.sleep(1000); // 模拟耗时的生产过程
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
// 创建消费者线程
Thread consumerThread = new Thread(() -> {
try {
while (true) {
example.consume();
Thread.sleep(1000); // 模拟耗时的消费过程
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
producerThread.start();
consumerThread.start();
}
}
代码解释:
-
同步方法:
produce()和consume()方法都被标记为synchronized,这确保了每次只有一个线程可以执行这些方法。这是必要的,因为它们修改共享数据(缓冲区和相关的计数器)。 -
等待和通知:使用
wait()方法让线程等待条件变为真。当生产者发现缓冲区已满时,它会调用wait()并释放锁,从而允许其他线程(如消费者)执行。消费者在缓冲区为空时也会这么做。当生产者放入一个项目或消费者取出一个项目时,它们会调用notifyAll()来唤醒所有等待的线程。 -
循环缓冲区:使用简单的数组实现循环缓冲区,其中
rear和front指针帮助在数组中环绕。
这个例子展示了如何使用 synchronized 和条件变量(wait() 和 notifyAll())来处理多线程中的生产者-消费者问题,确保线程安全并有效地使用资源。
假设一个生产者生产了之后执行该函数,即唤醒一个等待在该对象object的线程,如果这个线程是生产者,那么这个生产者有可能会发生阻塞,从而导致死锁。
设想在唤醒该生产者的前几个时刻,又有多个生产者被阻塞在等待队列上,那么就有可能唤醒其余生产者,而不是消费者。