A producer is a thread that creates data, and a consumer is a thread that uses data. When producers and consumers run as separate threads, the difference in processing speed between them becomes a problem. ** The data has not yet been created when the consumer tries to receive the data, or the consumer is not ready to receive the data when the producer tries to pass the data **. In the Producer-Consumer pattern, there is a "bridge" between producers and consumers. This bridge bridge fills the gap in processing speed between threads. When both the producer and the consumer are singular, it is sometimes called the ** Pipe pattern **.
Consider an example where three cooks make a cake and put it on the table, three customers eat it, and up to three cakes can be put on the table. If there are three cakes on the table and the cook tries to put more cakes on the table, the cook waits until there is room to put them on the table. If a customer tries to take a cake from the table when no cake is placed on the table, the customer will have to wait until the cake is placed.
(See this manual for the entire code)
MakerThread
public class MakerThread extends Thread {
...
private static int id = 0; //0082 Cake serial number(Common to all cocks)
...
public void run() {
try {
while (true) {
Thread.sleep(random.nextInt(1000));
String cake = "[ Cake No." + nextId() + " by " + getName() + " ]";
table.put(cake);
}
} catch (InterruptedException e) {
}
}
private static synchronized int nextId() {
return id++;
}
}
EaterThread
public class EaterThread extends Thread {
...
public void run() {
try {
while (true) {
String cake = table.take();
Thread.sleep(random.nextInt(1000));
}
} catch (InterruptedException e) {
}
}
...
Table
public class Table {
private final String[] buffer;
private int tail; //Where to put next
private int head; //Next place to take
private int count; //Number of cakes in buffer
...
//Put the cake
public synchronized void put(String cake) throws InterruptedException {
System.out.println(Thread.currentThread().getName() + " puts " + cake);
while (count >= buffer.length) {
wait();
}
buffer[tail] = cake;
tail = (tail + 1) % buffer.length;
count++;
notifyAll();
}
//Take the cake
public synchronized String take() throws InterruptedException {
while (count <= 0) {
wait();
}
String cake = buffer[head];
head = (head + 1) % buffer.length;
count--;
notifyAll();
System.out.println(Thread.currentThread().getName() + " takes " + cake);
return cake;
}
}
Execution result
...
MakerThread-1 puts [ Cake No.10 by MakerThread-1 ]
EaterThread-1 takes [ Cake No.10 by MakerThread-1 ]
MakerThread-1 puts [ Cake No.11 by MakerThread-1 ]
EaterThread-3 takes [ Cake No.11 by MakerThread-1 ]
MakerThread-3 puts [ Cake No.12 by MakerThread-3 ]
MakerThread-3 puts [ Cake No.13 by MakerThread-3 ]
EaterThread-2 takes [ Cake No.12 by MakerThread-3 ]
EaterThread-3 takes [ Cake No.13 by MakerThread-3 ]
...
Data role: Created by the Producer role and used by the Consumer role. In the example above, this is the cake. Producer role: Create a Data role and pass it to the Channel role. In the example above, this is the MakerThread class. Consumer role: Receive the Data role from the Channel role and use it. In the example above, this is the EaterThread class. Channel role: Receive the Data role from the Producer role, store it, and pass the Data role in response to the request from the Consumer role. Exclusive control is performed for access from the Producer role and the Consumer role. The Channel role is between the Producer role and the Consumer role, and plays the role of a relay point and a communication path for passing the Data role. In the example above, this is the Table class.
-** All the code that takes into consideration the behavior of multithreading is hidden in the Table class that plays the role of Channel. ** ** -** The Producer role does not depend on the progress of the Consumer role. ** ** -The fact that an exception called ** InterruptedException ** can be thrown means that it is a ** "time-consuming" method ** and ** a "cancellable" method **.
Even if the thread is waiting in wait, it can be canceled in the same way as sleep. You can use the interrupt method to tell the waiting thread, "You don't have to wait for notify / notifyAll anymore. Get out of the waitset." However, in the case of wait, you need to be careful about locking. The thread had released the lock once when it entered the weight set. The thread that was called interrupt during the wait (that is, the thread that was canceled) regains the lock and then throws an InterruptedException. That is, ** you cannot throw the exception InterruptedException until the lock is released **.
Relation Summary of "Design Patterns Learned in Java Language (Multithreaded Edition)" (Part 1) Summary of "Design Patterns Learned in Java Language (Multithreaded Edition)" (Part 2) Summary of "Design Patterns Learned in Java Language (Multithreaded Edition)" (Part 3) Summary of "Design Patterns Learned in Java Language (Multithread Edition)" (Part 4) Summary of "Design Patterns Learned in Java Language (Multithread Edition)" (Part 5) Summary of "Design Patterns Learned in Java Language (Multithread Edition)" (Part 6)
Recommended Posts