(1) ReentrantLock的条件对象
通常,线程进入临界区,却发现在某一条件满足之后才能执行,条件对象就是用来管理那些已经获得了锁,但是却不能做有用工作的线程。
一个锁对象可以有一个或多个相关的条件对象,我们可用lock.newCondition()方法获得一个条件对象。
ReentrantLock myLock = new ReentrantLock();
// 获得锁myLock的一个条件对象
Condition condition = myLock.newCondition();
下面我们直接通过代码来学习条件对象的使用。
以下代码实现的是简单的生产者和消费者案例:
public class ReentrantLockTest3 {
private ReentrantLock myLock = new ReentrantLock();
private Condition condition = myLock.newCondition();
private List<Integer> listBuffer = new ArrayList<Integer>();
private volatile boolean runFlag = true;
/**
* 生产者 生产数据
*/
public void produce() {
int i = 0;
while(runFlag) {
myLock.lock();
try {
// 生产者检查容器中是否有数据,如果容器中有数据则生产者等待
// 如果容器中没有数据则生产数据放入容器中并通知消费者
if (listBuffer.size() > 0) {
try {
// 调用await()方法,生产者线程阻塞并释放锁,之后进入该条件的等待集中
// 直到消费者调用signalAll()方法之后,生产者线程解除阻塞并重新竞争锁
// 生产者线程获得锁之后,重新开始从被阻塞的地方继续执行程序
condition.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
System.out.println(Thread.currentThread().getName() + " add Integer");
listBuffer.add(i++);
// 生产者线程调用signalAll()方法,通知消费者线程容器中有数据
condition.signalAll();
}
} finally {
myLock.unlock();
}
}
}
/**
* 消费者 读取数据
*/
public void consume() {
while(runFlag) {
myLock.lock();
try {
// 消费者检查容器中是否有数据,如果没有数据消费者等待
// 如果容器中有数据则读取数据,读完之后通知生产者
if (listBuffer.size() == 0) {
try {
// 同生产者线程一样,消费者线程调用await()方法阻塞并进入该条件等待集中
condition.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} else {
System.out.println(Thread.currentThread().getName() + " get Integer");
long beginTime = 0;
System.out.println(listBuffer.remove(0));
beginTime = System.currentTimeMillis();
while(System.currentTimeMillis() - beginTime < 100) {}
// 消费者线程调用signalAll()方法,通知生产者生产数据
condition.signalAll();
}
} finally {
myLock.unlock();
}
}
}
public boolean isRunFlag() {
return runFlag;
}
public void setRunFlag(boolean runFlag) {
this.runFlag = runFlag;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
final ReentrantLockTest3 test = new ReentrantLockTest3();
Thread produce = new Thread(new Runnable() {
public void run() {
test.produce();
}
},"A");
Thread consume = new Thread(new Runnable() {
public void run() {
test.consume();
}
},"B");
produce.start();
consume.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
test.setRunFlag(false);
}
}
condition.await():调用该方法的线程会被阻塞,同时释放持有的锁对象,之后线程进入该条件的等待集中。注意等待获得锁的线程和调用await()进入等待状态的线程不同。
等待获得锁的线程:如果锁可用的话,当线程获得cpu时间片后线程立即解除阻塞状态
调用await()进入等待状态的线程:当锁可用时,该线程不能立即解除阻塞状态,相反它一直处于阻塞状态,直到另一个线程调用同一条件上的signalAll()方法,它才解除阻塞状态。
condition.signalAll():该方法重新激活因为conditon这一条件而等待的所有线程。当这些线程从等待集中移出时,他们再次成为可运行的,调度器将再次激活它们,同时它们将试图重新进入该对象。一旦锁成为可用的,它们中的某个将从await()调用返回,获得锁并从被阻塞的地方继续执行。
注意:signalAll()方法不会立即激活一个等待的线程。它仅仅解除等待的线程阻塞,以便这些线程可以在当前线程退出同步方法之后,通过竞争实现对对象的访问。
避免死锁的问题:
当一个线程调用await()方法时,它没有办法重新激活自己进入可运行状态,它寄希望于其他线程调用signalAll()方法或signal()方法。如果没有其他线程来重新激活等待的线程,调用await()方法的线程就永远不会再运行了,这样就会导致“死锁”。
目前死锁问题 没有什么有效的机制来避免,只能通过我们谨慎的调用await()和signal()\singalAll() 方法。
分享到:
相关推荐
问题涉及至少两个线程:生产者线程和消费者线程。 - **是否有共享数据**:是。产品是生产者和消费者之间的共享数据。 - **是否有线程安全问题**:有。由于存在共享数据,需要确保线程安全。 - **是否需要考虑处理...
动力节点的Java课程适合绝对零基础的观看,教程中讲解了Java开发环境搭建、Java的基础语法、Java的面向对象。每一个知识点都讲解的非常细腻,由浅入深。适合非计算机专业,想转行做Java开发的朋友,或者想让Java基础...
在并发编程中,"生产者-消费者"模式是一种经典的解决问题的范式,用于协调两个或更多线程间的协作,其中一部分线程(生产者)生成数据,另一部分线程(消费者)消费这些数据。 生产者-消费者模型的核心在于共享资源...
总的来说,理解和掌握生产者消费者问题及其解决策略是Java多线程编程的重要部分,这不仅有助于编写高效、可靠的并发代码,也是提升软件设计能力的关键一步。通过不断实践和学习,开发者能够更好地应对复杂的并发场景...
"典型的多线程--生产和消费"这个主题,通常指的是生产者-消费者问题,这是一种经典的设计模式,用于解决如何在一个数据结构(如队列)中高效地存储和检索数据,同时避免生产者过于快速地生成数据而消费者无法及时...
"生产者-消费者"模型是多线程问题中的经典案例,它揭示了如何通过线程间的协作来实现数据共享和高效利用资源。今天我们将深入探讨这个主题。 生产者-消费者模型是由两个主要角色构成:生产者和消费者。生产者负责...
生产者消费者模型是一种经典的线程同步问题,它模拟了实际生活中的生产过程和消费过程,使得生产者线程可以将数据生产出来,而消费者线程则负责消耗这些数据,两者之间通过共享数据结构进行协同工作。 生产者消费者...
然后,我们需要创建生产者和消费者线程。每个生产者线程会随机生成产品并调用`produce()`方法,而消费者线程则不断调用`consume()`方法。为了实现多个线程,我们可以使用`Thread`类或者`java.util.concurrent....
【生产者消费者问题】是多线程编程中的经典问题,主要涉及到进程同步和互斥的概念。在操作系统中,生产者负责生成数据,而消费者则负责消耗这些数据。两者通过一个有限大小的缓冲区进行交互,当缓冲区满时,生产者...
该问题旨在模拟一个系统,其中多个生产者线程生产产品,而多个消费者线程消费这些产品,同时需要确保生产与消费的同步,防止缓冲区溢出或空洞。 描述中提到,这份报告是作者认真完成的,所有题目正确无误,最终获得...
在操作系统实验中,实现生产者-消费者模型可以帮助我们理解线程的同步机制,如互斥锁(mutex)和信号量(semaphore)。下面我们将详细探讨这两个概念以及如何在多线程环境下应用它们。 1. **互斥锁**:互斥锁是一种...
"生产者-消费者"模式是多线程编程中的一个经典设计模式,它体现了线程间的协作和同步。在这个模式中,"生产者"线程负责创建资源,而"消费者"线程则负责消耗这些资源。这个模式在实际应用中非常常见,例如在消息队列...
在Linux环境下进行多线程编程时,一个常见的应用场景就是通过生产者消费者模型来管理线程间的通信和数据共享问题。本篇文章将详细解析一个基于Linux环境下的多线程示例程序,该程序通过生产者消费者模型实现数据的...
生产者-消费者问题是操作系统中经典的问题之一,它是指在多线程环境下,多个生产者线程和消费者线程访问同一个共享缓冲区,导致缓冲区的数据混乱和不一致的问题。 在解决生产者-消费者问题时,需要使用同步机制来...
生产者-消费者模型涉及到两个主要角色:生产者(Producer)和消费者(Consumer)。生产者负责生成数据或产品,并将其放入一个共享的缓冲区;而消费者则从这个缓冲区中取出并消费这些数据或产品。在我们的例子中,有...
生产者-消费者实例可以很好帮助初学java的同学们理解和学习多线程的知识
本文将详细地阐述 Linux 下利用互斥锁、条件变量、线程管理等相关函数实现多线程生产者-消费者问题,以提高资源利用率。 线程基本概念 线程是进程的一条执行路径,它包含独立的堆栈和 CPU 寄存器状态,每个线程...
创建一个简单的生产者消费者模型,可以使用以下伪代码: ```java class Producer implements Runnable { private final BlockingQueue<String> queue; public Producer(BlockingQueue<String> queue) { this....
设计要求:(1)每个生产者和消费者对有界缓冲区进行操作后,即时显示有界缓冲区的全部内容,当前指针位置和生产者/消费者线程的标识符.(2)生产者和消费者各有两个以上.(3)多个生产者或多个消费者之间须有共享对缓冲区...
4. **互斥与同步**:确保生产者与消费者之间能够正确地进行互斥和同步操作,即防止数据竞争并保证消息的有序处理。 5. **实现方式**:通过线程和进程两种方式实现并发控制模型。 6. **测试方案**:设计不同的...