线程安全的生产消费模型是指在多线程环境中,生产者和消费者能够正确、稳定地协同工作。在生产过程中,线程安全的生产者线程会向队列中添加数据,而线程安全的消费者线程会从队列中消费数据。这种模型的主要特点是:
- 生产者线程和消费者线程能够并发地执行,不会出现竞争状态。
- 生产者线程能够正确地向队列中添加数据,即使队列已满。
- 消费者线程能够正确地从队列中取出数据,即使队列为空。
- 线程安全的生产消费模型能够保证数据的正确性和一致性。
实现线程安全的生产消费模型的常用方法是通过使用锁或信号量来保护共享资源。例如,可以使用互斥锁来保护队列的访问,从而确保生产者线程和消费者线程不会同时访问队列。另外,可以使用条件变量来实现队列的阻塞和唤醒操作,以便生产者和消费者能够正确地等待和获取数据。
import java.util.LinkedList;
public class ProducerConsumerExample {
public static void main(String[] args) {
// 创建一个共享队列
LinkedList<Integer> sharedQueue = new LinkedList<>();
// 创建一个生产者和一个消费者
Thread producer = new Thread(new Producer(sharedQueue));
Thread consumer = new Thread(new Consumer(sharedQueue));
// 启动生产者和消费者线程
producer.start();
consumer.start();
}
}
class Producer implements Runnable {
private final LinkedList<Integer> sharedQueue;
private final int MAX_SIZE = 5;
public Producer(LinkedList<Integer> sharedQueue) {
this.sharedQueue = sharedQueue;
}
@Override
public void run() {
int value = 0;
while (true) {
synchronized (sharedQueue) {
// 如果队列已满,等待
while (sharedQueue.size() == MAX_SIZE) {
try {
System.out.println("Queue is full, producer thread is waiting.");
sharedQueue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 否则将值放入队列,通知消费者线程
System.out.println("Producer produced: " + value);
sharedQueue.add(value++);
sharedQueue.notify();
}
}
}
}
class Consumer implements Runnable {
private final LinkedList<Integer> sharedQueue;
public Consumer(LinkedList<Integer> sharedQueue) {
this.sharedQueue = sharedQueue;
}
@Override
public void run() {
while (true) {
synchronized (sharedQueue) {
// 如果队列为空,等待
while (sharedQueue.isEmpty()) {
try {
System.out.println("Queue is empty, consumer thread is waiting.");
sharedQueue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 否则从队列中取出值,通知生产者线程
int value = sharedQueue.poll();
System.out.println("Consumer consumed: " + value);
sharedQueue.notify();
}
}
}
}
在这个示例中,我们创建了一个共享队列,然后创建了一个生产者和一个消费者线程。生产者线程将值放入共享队列,而消费者线程则从队列中取出值。通过使用synchronized
和wait()
/notify()
方法,我们确保生产者和消费者线程可以正确地协作,以避免数据竞争和死锁等问题。
当然,你也可以使用Lock类来创建一个锁,示例如下:
Condition是Java提供了来实现等待/通知的类,Condition类还提供比wait/notify更丰富的功能,Condition对象是由lock对象所创建的。但是同一个锁可以创建多个Condition的对象,即创建多个对象监视器。这样的好处就是可以指定唤醒线程。notify唤醒的线程是随机唤醒一个。
package sty.Thread;
import java.util.LinkedList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ProducerConsumerExample2 {
public Condition notEmpty;
public Condition notFull;
public void main(String[] args) {
// 创建一个共享队列
LinkedList<Integer> sharedQueue = new LinkedList<>();
Lock lock = new ReentrantLock();
Condition notEmpty = lock.newCondition();
Condition notFull = lock.newCondition();
// 创建一个生产者和一个消费者
Thread producer = new Thread(new Producer(sharedQueue, lock));
Thread consumer = new Thread(new Consumer(sharedQueue, lock));
// 启动生产者和消费者线程
producer.start();
consumer.start();
}
class Producer implements Runnable {
private final LinkedList<Integer> sharedQueue;
private final int MAX_SIZE = 5;
public Lock lock;
public Producer(LinkedList<Integer> sharedQueue,Lock lock) {
this.sharedQueue = sharedQueue;
this.lock = lock;
}
@Override
public void run() {
int value = 0;
while (true) {
// 如果队列已满,等待
//Lock要和condiction配合起来用
lock.lock();
try {
while (sharedQueue.size() == MAX_SIZE) {
System.out.println("Queue is full, producer thread is waiting.");
//如果队列满则阻塞 生产者线程
notFull.await();
}
// 将值放入队列,通知消费者线程
System.out.println("Producer produced: " + value);
sharedQueue.add(value++);
notEmpty.signal();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();//解除锁定
}
}
}
}
class Consumer implements Runnable {
private final LinkedList<Integer> sharedQueue;
public Lock lock;
public Consumer(LinkedList<Integer> sharedQueue,Lock lock) {
this.sharedQueue = sharedQueue;
this.lock = lock;
}
@Override
public void run() {
while (true) {
lock.lock();
// 如果队列为空,等待
try {
while (sharedQueue.isEmpty()) {
System.out.println("Queue is empty, consumer thread is waiting.");
//如果队列空则阻塞 消费者线程
notEmpty.await();
}
// 从队列中取出值,通知生产者线程
int value = sharedQueue.poll();
System.out.println("Consumer consumed: " + value);
notFull.await();
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.lock();
}
}
}
}
}
在Java中,一个线程可以通过synchronized
关键字来获取一个对象的监视器(monitor),并调用wait()
方法来等待该对象的监视器上发生的事件,直到其他线程调用该对象的notify()
或notifyAll()
方法来唤醒它。下面是一个使用wait()
和notify()
方法来控制线程等待和唤醒的示例:
public class WaitNotifyExample {
public static void main(String[] args) {
final Object lock = new Object(); // 创建一个共享对象锁
// 创建一个等待线程
Thread waitingThread = new Thread(() -> {
synchronized (lock) { // 获取共享对象锁
try {
System.out.println("Waiting thread is waiting...");
lock.wait(); // 等待其他线程唤醒
System.out.println("Waiting thread is notified.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 创建一个唤醒线程
Thread notifyingThread = new Thread(() -> {
synchronized (lock) { // 获取共享对象锁
System.out.println("Notifying thread is notifying...");
lock.notify(); // 唤醒等待线程
}
});
// 启动等待线程和唤醒线程
waitingThread.start();
notifyingThread.start();
}
}
需要注意的是,wait()和notify()都需要在线程安全的情况下操作,如放在synchronized控制的代码块中或使用Lock加锁后,并且因为这两个方法都是监视器下的方法,因此调用时也需要注意使用同一个监视器。