1, Producer consumer issues
1. Problems
- Suppose that only one product can be put in the warehouse, the producer puts the produced products into the warehouse, and the consumer takes the products from the warehouse for consumption
- If there is no product in the warehouse, the producer puts the product into the warehouse. Otherwise, stop production and wait until the products in the warehouse are taken away by consumers
- If there is a product in the warehouse, the consumer can take the product away for consumption, otherwise stop consumption and wait until the product is put in the warehouse again
2. Analysis
Producers and consumers share the same resource, and they are interdependent and conditional on each other
- For producers, inform consumers to wait before producing products. After the product is produced, consumers need to be notified immediately
- For consumers, after consumption, they should inform producers that they have finished consumption and need to produce new products
- In the producer consumer problem, synchronized is not enough
- synchronized prevents concurrent updates to the same shared resource, enabling synchronization
- synchronized cannot be used to communicate between different threads
2, Method of thread communication
Java provides several methods to solve the communication problem between threads
Method name | effect |
---|---|
wait() | Indicates that threads wait consistently until notified by other threads. Unlike sleep(), locks are released |
wait(long timeout) | Specifies the number of milliseconds to wait |
notify() | Wake up a waiting thread |
notifyAll() | Wake up all threads calling the wait() method on the same object, and the threads with high priority are scheduled first |
Note: the above methods are all Object class methods and can only be used in synchronization methods or synchronization code blocks, otherwise an illegalmonitorstateexception will be thrown
3, Solution
1. Tube pass method
- Producer: the module responsible for production data (possibly method, object, thread, process)
- Consumer: the module responsible for processing data (possibly method, object, thread, process)
- Buffer: consumers cannot directly use the producer's data. There is a "buffer" between them
The producer puts the produced data into the buffer, and the consumer takes out the data from the buffer
[the external link image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-dldldlfijg-1627894061169) (image / image-20210801205129366. PNG)]
Example: public class TestPC { public static void main(String[] args) { SyncContainer container = new SyncContainer(); new Producer(container).start(); new Consumer(container).start(); } } class Producer extends Thread { SyncContainer container; public Producer(SyncContainer container) { this.container = container; } @Override public void run() { // production for (int i = 0; i < 100; i++) { container.push(new Chicken(i)); System.out.println("Produced" + i + "Chicken"); } } } class Consumer extends Thread { SyncContainer container; public Consumer(SyncContainer container) { this.container = container; } @Override public void run() { // consumption for (int i = 0; i < 100; i++) { System.out.println("Consumed the second" + container.pop().id + "Chicken"); } } } class Chicken { int id; public Chicken(int id) { this.id = id; } } class SyncContainer { // A container size is required Chicken[] chickens = new Chicken[10]; // Container counter int count = 0; // The producer puts in the product public synchronized void push(Chicken c) { // If the container is full, wait for the consumer to consume if (count == chickens.length) { // Inform consumers and producers to wait try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // If it is not full, it needs to throw in the product chickens[count] = c; count++; // Inform consumers that they can consume this.notifyAll(); } // Consumer products public synchronized Chicken pop() { // Judge whether it can be consumed if (count == 0) { // Waiting for production try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // If you can consume count--; Chicken chicken = chickens[count]; // Inform the producer of production this.notifyAll(); return chicken; } }
2. Signal lamp method
Judging by the flag bit, if it is a true consumer waiting, if it is a false producer waiting
Example: public class TestPC2 { public static void main(String[] args) { TV tv = new TV(); new Player(tv).start(); new Watcher(tv).start(); } } // Producer actor class Player extends Thread { TV tv; public Player(TV tv) { this.tv = tv; } @Override public void run() { for (int i = 0; i < 20; i++) { if (i % 2 == 0) { this.tv.play("Happy base camp"); } else { this.tv.play("advertisement"); } } } } // Consumer audience class Watcher extends Thread { TV tv; public Watcher(TV tv) { this.tv = tv; } @Override public void run() { for (int i = 0; i < 20; i++) { tv.watch(); } } } // Products - programs class TV { // While the actor was recording, the audience waited for T // As the audience watched, the actor waited for F // Recorded programs String show; // Flag bit boolean flag = true; // Actor recording public synchronized void play(String show) { if (!flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("The actor recorded it" + show); // Inform the audience to watch this.notifyAll(); this.show = show; this.flag = !this.flag; } // Audience watching public synchronized void watch() { if (flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("The audience watched" + show); // Notify actors to record this.notifyAll(); this.flag = !this.flag; } }
4, Summary
- The "lock" based synchronization method requires the thread to constantly try to obtain the lock. If it fails, it also needs to continue to try, which will waste resources, and the wait / notification mechanism can solve this problem
- The wait / notify mechanism uses the same object lock. If two threads use different object locks, they cannot communicate with this mechanism
5, Expand
Semaphore
- Keyword volatile, which can ensure the visibility of memory
- For variables decorated with volatile, if the value changes in one thread, it will be immediately visible in other threads
- Variables decorated with volatile require atomic operations