10, Producer consumer issues

Posted by aeshanw on Tue, 04 Jan 2022 16:54:46 +0100

1, Producer consumer issues

1. Problems

  1. 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
  2. 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
  3. 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 nameeffect
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

Topics: Java Multithreading