Let’s start with an example

In a noodle shop, there is a chef and a diner who make noodles. We need to ensure that the chef makes a bowl of noodles and the diner eats a bowl of noodles. We can’t make several bowls of noodles at a time, and we can’t eat noodles without noodles. According to the above operations, for ten rounds of noodle eating operation.

Speak in code

First we need to have a resource class, which contains the number of surface, do surface operations, eat surface operations; When the number of noodles is 0, the chef can only make noodles. After finishing noodles, the chef needs to wake up the waiting diners. Otherwise, the chef needs to wait for the diners to finish eating noodles before making noodles. When the number of noodles is not 0, diners can eat noodles. After eating noodles, they need to wake up the chef who is waiting. Otherwise, diners need to wait for the chef to finish noodles before eating noodles. Then in the main class, we create a cook thread to cook noodles 10 times and a diner thread to eat noodles 10 times. The code is as follows:

package com.duoxiancheng.code;

/ * * *@user: Code essay */

class Noodles{

    // The number of faces
    private int num = 0;

    // Make noodles
    public synchronized void makeNoodles(a) throws InterruptedException {
        // If the number of noodles is not zero, wait for diners to finish the noodles before making noodles
        if(num ! =0) {this.wait();
        }

        num++;
        System.out.println(Thread.currentThread().getName()+"I've made a noodle. I've got it."+num+"A face");
        // When the noodles are ready, wake up the diners to eat
        this.notifyAll();
    }

    // How to eat noodles
    public synchronized void eatNoodles(a) throws InterruptedException {
        // If the number of noodles is 0, wait for the chef to finish the noodles before eating the noodles
        if(num == 0) {this.wait();
        }

        num--;
        System.out.println(Thread.currentThread().getName()+"I ate a noodle. I have it."+num+"A face");
        // Wake up the cook to cook noodles
        this.notifyAll(); }}public class Test {

    public static void main(String[] args) {

        Noodles noodles = new Noodles();

        new Thread(new Runnable(){
            @Override
            public void run(a) {
                try {
                    for (int i = 0; i < 10; i++) { noodles.makeNoodles(); }}catch(InterruptedException e) { e.printStackTrace(); }}},"A cook").start();

        new Thread(new Runnable(){
            @Override
            public void run(a) {
                try {
                    for (int i = 0; i < 10; i++) { noodles.eatNoodles(); }}catch(InterruptedException e) { e.printStackTrace(); }}},"Diner A").start(); }}Copy the code

The output is as follows:

You can see it’s alternating;

What if you have two chefs, two diners, both doing 10 cycles?

Create two more threads in the main class. The main class code is as follows:

public class Test {

    public static void main(String[] args) {

        Noodles noodles = new Noodles();

        new Thread(new Runnable(){
            @Override
            public void run(a) {
                try {
                    for (int i = 0; i < 10; i++) { noodles.makeNoodles(); }}catch(InterruptedException e) { e.printStackTrace(); }}},"A cook").start();

        new Thread(new Runnable(){
            @Override
            public void run(a) {
                try {
                    for (int i = 0; i < 10; i++) { noodles.makeNoodles(); }}catch(InterruptedException e) { e.printStackTrace(); }}},"B" cook).start();

        new Thread(new Runnable(){
            @Override
            public void run(a) {
                try {
                    for (int i = 0; i < 10; i++) { noodles.eatNoodles(); }}catch(InterruptedException e) { e.printStackTrace(); }}},"Diner A").start();

        new Thread(new Runnable(){
            @Override
            public void run(a) {
                try {
                    for (int i = 0; i < 10; i++) { noodles.eatNoodles(); }}catch(InterruptedException e) { e.printStackTrace(); }}},"Diner B").start(); }}Copy the code

The output is as follows:

False awaken

The problem above is “false awakening”. When we have only one cook and one diner, it can only be the cook making the noodles or the diner eating the noodles, there is no other situation; But when there are two cooks, two diners, the following problem arises:

  1. The initial state

2. Cook A gets the operation right, finds the number of noodles is 0, can cook noodles, the number of noodles +1, then wake up all threads;

3. Cook B has the right to operate the noodle, and performs wait operation when the number of noodles is 1.4. Cook A has the right to operate the noodle, and execute the wait operation because the number of noodles is 1.5. Diner A gets the operation right, finds the number of noodles is 1, can eat noodles, after eating noodles number -1, and wakes up all threads;

6. At this time, chef A gets the operation right, because it continues to run from the blocked place, there is no need to judge whether the number of faces is 0, so the number of faces +1, and wake up other threads;

7. Cook B now has the operation right, because it continues to run from the block, there is no need to judge whether the number of faces is 0, so the number of faces +1, and wake up other threads;This is false awakening, and there are other cases that readers can try to draw and analyze.

The solution

The reason for false wake up is that there is no judgment from blocked state to ready state to run state, we just need to make it judge every time it gets the right to operate; So will

if(num ! =0) {this.wait();
}
Copy the code

Instead of

while(num ! =0) {this.wait();
}
Copy the code

will

if(num == 0) {this.wait();
}
Copy the code

Instead of

while(num == 0) {this.wait();
}
Copy the code

Can.

Wechat search: Code essay welcome to pay attention to the output of Java, algorithms and other dry technical public number.