Thread false wake-up in JUC concurrent programming

Posted by jeremywesselman on Sat, 09 Oct 2021 06:11:55 +0200

Compilation of producer consumer code

I believe everyone is very familiar with producers and consumers. Here, simply write about producers and consumers in the concurrent environment to see what problems they will have!

Concurrent programming steps are very simple steps. To sum up, they are thread operation resource classes!
2. Create a resource class. Lock lock is used here

//Establish resource classes and related attributes and methods,
public class Resource {
    private int number =0;
    private Lock lock=new ReentrantLock();
    private Condition condition=lock.newCondition()
 public void incr1() throws Exception{
        lock.lock();
   try {

   }finally {
       lock.unlock();
   }
    }
    public void decr1()throws Exception{
        lock.lock();
        try {;
        }finally {
           lock.unlock();
        }
    }
    };

A little hint. For safety, we'd better put the unlocked in finally! Even if there is an exception, he will release the lock without deadlock!

  1. Judgment plus work
Determine whether the current thread should proceed to the next step
public class Resource {
    private int number =0;
    private Lock lock=new ReentrantLock();
    private Condition condition=lock.newCondition()
 public void incr1() throws Exception{
        lock.lock();
   try {
 if (number!=0){
           condition.await();
       }
       number++;
       System.out.println(Thread.currentThread().getName()+"::"+number);
       condition.signalAll();
   }finally {
       lock.unlock();
   }
    }
    public void decr1()throws Exception{
        lock.lock();
        try {;
         if (number!=1){
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName()+"::"+number);
            condition.signalAll();
        }finally {
           lock.unlock();
        }
    }
    };
  1. test

Here are some inline snippets.

test
public class JucLock {
    public static void main(String[] args) {
        Resource resource = new Resource();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    resource.decr1();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"A thread ").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    resource.incr1();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"B thread ").start();
        }

5. Results

Simply explain: the declared addition and subtraction methods are locked. Each method judges when a thread comes in, and then checks whether the conditions are met to work! We can see from the above that there is basically no problem when two threads operate. When there are more competitors, there will be some concurrency problems!
Here are some inline snippets.

The emergence of concurrency problems
// An highlighted block
public class JucLock {
    public static void main(String[] args) {
        Resource resource = new Resource();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    resource.decr1();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"A thread ").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    resource.incr1();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"B thread ").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    resource.decr1();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"C thread ").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    resource.incr1();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"D thread ").start();
    }
}
;


When you see the above figure, there are related problems. Generally speaking, it should be 0 and 1 false alternate execution! But there are negative numbers and greater than 1
The problem is that if determines the position. Imagine that there are four abcd threads here. ac is the add operation and bd is the subtract operation. When the initial value of the variable is 0, if thread a grabs the lock and enters the judgment for the add operation, it will notify other threads after the operation is completed. At this time, the c thread grabs the execution right again. If it judges that the value is not 0, it calls the await method to wait. In the process of waiting, release the lock. If it is robbed of the execution right by bd one of the threads at this time, subtract one, and then notify other threads! So here's the point. If the execution right is robbed by thread a at this time, and it happens to be robbed by the waiting thread c when notifying other threads, it is awakened! At this time, the c thread will be awakened directly at the position of await method, and it will add one without judgment, so it will become 2
When the concurrency is particularly large, it may be added to a larger number, and similarly, it may be reduced to a large negative number!
In order to solve this problem, the official document tells us to use loop judgment. When each thread grabs the execution right, it will judge whether it meets the conditions for the next operation, so as to avoid the problem!!!

Final code
public class Resource {
    private int number =0;
    private Lock lock=new ReentrantLock();
    private Condition condition=lock.newCondition();
    //producer

    public void incr1() throws Exception{
        lock.lock();
   try {
       while (number!=0){
           condition.await();
       }
       number++;
       System.out.println(Thread.currentThread().getName()+"::"+number);
       condition.signalAll();
   }finally {
       lock.unlock();
   }
    }
    public void decr1()throws Exception{
        lock.lock();
        try {
            while (number!=1){
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName()+"::"+number);
            condition.signalAll();
        }finally {
           lock.unlock();
        }
    }
}

Topics: Java