Threads - Producers and Consumers

(1) Condition object of ReentrantLock
Usually thread enters a critical section, but finds that it can only be executed after a certain condition is met. The condition object is used to manage those threads that have acquired the lock but cannot do useful work.
A lock object can have one or more related condition objects, and we can obtain a condition object with the lock.newCondition() method.
ReentrantLock myLock = new ReentrantLock();
// Obtain a condition object of the lock myLock
Condition condition = myLock.newCondition();
Let's learn the use of condition objects directly through the code.
The following code implements a simple producer and consumer case:
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;
/**
* Producer produces data
*/
public void produce() {
int i = 0;
while(runFlag) {
myLock.lock();
try {
// The producer checks whether there is data in the container, if there is data in the container, the producer waits
// If there is no data in the container, the production data is put into the container and the consumer is notified If ( listBuffer.size
() > 0) {
try {
// call the await() method, the producer thread blocks and releases the lock, and then enters the waiting set for the condition
// until the consumer calls the signalAll() method, the production The producer thread unblocks and competes for the lock again
// After the producer thread acquires the lock, it resumes execution from the blocked place
condition.await();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e. printStackTrace();
}
} else {
System.out.println(Thread.currentThread().getName() + " add Integer");
listBuffer.add(i++);
// The producer thread calls the signalAll() method to notify the consumer The author thread container has data
condition.signalAll();
}
} finally {
myLock.unlock();
}
}
}

/**
* Consumer reads data
*/
public void consume() {
while(runFlag) {
myLock.lock();
try {
// Consumer checks if the container is in If there is data, if there is no data, the consumer waits
// If there is data in the container, read the data, and notify the producer after reading
if (listBuffer.size() == 0) {
try {
// Same as the producer thread, consume The operator thread calls the await() method to block and enters the condition waiting set
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) {}
// The consumer thread calls the signalAll() method to notify the producer Produce data
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(): The thread calling this method will be blocked and the lock object held will be released, and then the thread will enter the waiting set for the condition. Note that the thread waiting to acquire the lock is different from the thread that calls await() to enter the waiting state.
Threads waiting to acquire a lock: if the lock is available, the thread unblocks immediately after the thread acquires the cpu time slice
A thread that calls await() to enter the waiting state: When the lock is available, the thread cannot be unblocked immediately, instead it remains blocked until another thread calls the signalAll() method on the same condition.

condition.signalAll(): This method reactivates all threads waiting due to the condition of the condition. When these threads are removed from the wait set, they become runnable again, the scheduler will activate them again, and they will try to re-enter the object. Once the lock becomes available, one of them will return from the await() call, acquire the lock and continue execution from where it was blocked.
Note: The signalAll() method does not immediately activate a waiting thread. It simply unblocks waiting threads so that those threads can contend for access to the object after the current thread exits the synchronized method.


Avoid deadlock problems:
when a thread calls the await() method, it has no way to reactivate itself into a runnable state, it hopes that other threads call the signalAll() method or the signal() method. If there is no other thread to reactivate the waiting thread, the thread calling the await() method will never run again, causing a "deadlock".
At present, there is no effective mechanism to avoid the deadlock problem, and we can only call the await() and signal()\singalAll() methods carefully.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326205309&siteId=291194637