Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.


Halo, everybody is good, I am a countryside, recently wrote a blog before finishing, the summary and the content of a bloated, delete, and rewrite or want some blog content is of high quality, this blog son want to write a write the producer consumer problem in concurrent programming, before the interview was asked, I also give this issue summarizes the thinking of a very good understanding, “waiting for, Business, wake up “anyway! Just watch!


1. First things first

The producer-consumer problem needs to be understood the most. One thread needs the cooperation of another thread, just like the relationship between food preparation and cooking. The food needs to be prepared before cooking, which is realized by communication between threads. Each thread has its own waiting execution conditions, the business to be executed, and the wake up of other threads after the completion of business execution, which can be simply summarized as “Waiting, Business, Wake up”.

  • Synchronized thread communication useswait()notifyall()methods
  • The Lock is in Conditionawait()andsignalAll()methods

(Note: the signal() method is also useful for targeted wake up in multiple conditions.)

It is important to remember that while() is used instead of if() when judging the wait condition, and while() is used to prevent false wake up problems because while() checks the condition all the time, whereas if() only checks once


2. Use synchronized to print digits from 0 to 9 alternately

Synchronized synchronized synchronized synchronized synchronized synchronized synchronized synchronized synchronized synchronized synchronized synchronized
public class ProducerAndConsumer01 {

    public static void main(String[] args) {
        Task task = new Task();

        // Create thread A that prints even numbers
        new Thread(() -> {
            try {
                for (int i = 0; i < 5; i++) { task.evenNumber(); }}catch(Exception e) { e.printStackTrace(); }},"A").start();

        // create thread B that prints odd numbers
        new Thread(() -> {
            try {
                for (int i = 0; i < 5; i++) { task.oddNumber(); }}catch(Exception e) { e.printStackTrace(); }},"B").start(); }}class Task {

    private int num = 0;

    /** * prints odd */
    public synchronized void oddNumber(a) throws InterruptedException {
        // Wait condition, while condition used
        while (num % 2= =0) {
            this.wait();
        }

        // Perform business, number + 1 and print
        System.out.println(Thread.currentThread().getName() + "- >" + num++);

        // Wake up the thread that prints even numbers
        this.notifyAll();
    }

    /** * prints even */
    public synchronized void evenNumber(a) throws InterruptedException {
        // Wait for conditions
        while (num % 2= =1) {
            this.wait();
        }

        // Perform business, number + 1 and print
        System.out.println(Thread.currentThread().getName() + "- >" + num++);

        // Wake up the thread that prints odd numbers
        this.notifyAll(); }}Copy the code
  • The results


3. Use ReentrantLock to realize digital printing between three threads

  • The Condition of ReentrantLock is used to print digits from 0 to 14 alternately
  • Note: the await method will release the lock, and the three threads will continue to grab the lock. After obtaining the lock, they will judge the execution condition, and only when the execution condition is satisfied can they execute the business!!
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ProducerAndConsumer02 {
    public static void main(String[] args) {
        Task02 task = new Task02();

        new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                task.printA();
            }
        }).start();

        new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                task.printB();
            }
        }).start();

        new Thread(() -> {
            for (int i = 0; i < 5; i++) { task.printC(); } }).start(); }}/** ** 3 threads, print digits in turn ** note: "await" method will release the lock, after obtaining the lock to determine the execution condition, only when the execution condition is satisfied!! * /
class Task02 {
    // lock locks and three different execution conditions
    private final Lock lock = new ReentrantLock();
    private final Condition condition1 = lock.newCondition();
    private final Condition condition2 = lock.newCondition();
    private final Condition condition3 = lock.newCondition();

    // execute flag bit, 0,1,2 are the execution conditions of three threads respectively
    private int flag = 0;

    private int num = 0;

    public void printA(a) {
        lock.lock();

        try {
            // Wait for conditions
            while(flag ! =0) {
                condition1.await();
            }

            // Perform business
            System.out.println("Hello, I am." + Thread.currentThread().getName() + "I'm going to print." + num++);
            flag = 1;

            / / wake
            condition2.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally{ lock.unlock(); }}public void printB(a) {
        lock.lock();

        try {
            / / wait for
            while(flag ! =1) {
                condition2.await();
            }

            // Perform business
            System.out.println("Hey, Big Ga, I am." + Thread.currentThread().getName() + "I'm going to print." + num++);
            flag = 2;

            / / wake
            condition3.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally{ lock.unlock(); }}public void printC(a) {
        lock.lock();

        try {
            / / wait for
            while(flag ! =2) {
                condition3.await();
            }

            // Perform business
            System.out.println("Thunder Monkey, I am." + Thread.currentThread().getName() + "I'm going to print." + num++);
            flag = 0;

            / / wake
            condition1.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally{ lock.unlock(); }}}Copy the code
  • The results

3.1 Perform process analysis

  1. whenThread 0 gets the lockThread 1 and thread 2 will block. When the business is finished,Thread 0 releases the lock
  2. If at this timeThread 2 takes the lockAccording to the waiting condition, it will enter the waiting stateThread 2 releases the lock
  3. Now it is up toThread 1 gets the lockAccording to the wait condition, it will execute the transaction, and thenReady to wake up thread 2Why do you sayTo prepareWake up? Because at this timeThread 1 has not released the lockUntil theAfter thread 1 releases the lock.Thread 2 can get the lockIs actually woken up so that thread 2 can do its business

The above shows the process of printing the first three numbers in three threads. If you think about it, you can understand


4. Lightweight alternate number printing with Volatile

Go straight to the code! Very simple!

/** * Implement producer consumer */ with volatile
public class ProducerAndConsumer03 {
    public static void main(String[] args) {
        Task03 task = new Task03();

        new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                task.oddNumber();
            }
        }).start();

        new Thread(() -> {
            for (int i = 0; i < 5; i++) { task.evenNumber(); } }).start(); }}class Task03 {

    /** * the execution flag bit */
    private volatile int flag = 0;

    /** * Print the number */
    private int num = 1;

    /** * prints odd */
    public void oddNumber(a) {
        // Wait for conditions
        while(flag ! =0) {}/ / business
        System.out.println(Thread.currentThread().getName() + "- >" + num++);

        / / wake
        flag = 1;
    }

    /** * prints even */
    public void evenNumber(a) {
        // Wait for conditions
        while(flag ! =1) {}/ / business
        System.out.println(Thread.currentThread().getName() + "- >" + num++);

        / / wake
        flag = 0; }}Copy the code
  • The results

  • Perform process analysis: Variables marked as volatile are volatile to individual threadsvisibleTo take advantage of this feature, define a flag bit to control thread execution

Charge!