False awaken

public class Tick {
    public static void main(String[] args) {
        Data date = new Data();
        // Create 4 threads, two plus, two minus
        new Thread(()->{
            try {
                for (int i=0; i<20; i++) date.increment(); }catch(InterruptedException e) { e.printStackTrace(); }},"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    date.decrement();
                } catch(InterruptedException e) { e.printStackTrace(); }}},"B").start();
        new Thread(()->{
            try {
                for (int i=0; i<20; i++) date.increment(); }catch(InterruptedException e) { e.printStackTrace(); }},"C").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    date.decrement();
                } catch(InterruptedException e) { e.printStackTrace(); }}},"D").start(); }}class Data{
    private int number=0;
    public synchronized   void increment(a) throws InterruptedException {
        if(number! =0) // If there is more, wait
            this.wait();
        number++;
        System.out.println(Thread.currentThread().getName()+" incr->"+number);
        this.notify();
    }
    public synchronized void decrement(a)throws InterruptedException{
        if (number==0) // If not, wait
            this.wait();
        number--;
        System.out.println(Thread.currentThread().getName()+" decr->"+number);
        this.notify(); }}Copy the code

Running results:

. C incr->1 A incr->2 C incr->3......Copy the code

The reason:

  • When we use wait() in if, we can’t go wrong if we keep the ++, — order.
  • If thread C acquires the lock while thread A is waiting and releases the lock after ++, thread A acquires the lock. Then the code after A directly waits does not make A conditional judgment, so it fails.

Solution:

Use while instead of if for example

public synchronized void decrement(a)throws InterruptedException{
        while (number==0) // If not, wait
            this.wait();
        number--;
        System.out.println(Thread.currentThread().getName()+" decr->"+number);
        this.notify();
    }
Copy the code

Precise wake up single

Note for implementation of official document Single:

When this method is called, the implementation may (usually) require that the current thread save lock execution with the Condition must record this prerequisite and do anything if the lock is not held. Typically, an exception will be thrown if IllegalMonitorStateException.Copy the code
  1. Define multiple conditions and create multiple wait queues
  2. Condition. Single is used to wake up the longest waiting thread in the queue, which can be accurately woken up because there is only one thread waiting in multiple queues
public class TestCondistion {
    public static void main(String[] args) {
        Data3 date = new Data3();
        // Set up the ABCD thread, call printA, B, C, D respectively
        new Thread(()->{
            try {
                for (int i=0; i<20; i++) date.printA(); }catch(InterruptedException e) { e.printStackTrace(); }},"A").start();
        // omit similar code}}class Data3{
    private int number=0;
    private Lock lock=new ReentrantLock();
    // Create multiple conditions
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    private Condition condition4 = lock.newCondition();

    public  void printA(a) throws InterruptedException {
        lock.lock();
        try{
            while(number! =0)
                condition1.await();
            number++;
            System.out.println(Thread.currentThread().getName()+" incr->"+number);
            Condition2 is the one that condition2 is waiting for the longest. Condition2 is the only one that condition2 is waiting for
            condition2.signal();
        }catch (Exception exception){
            System.err.println("incr wrong");;
        }finally{ lock.unlock(); }}public void printB(a)throws InterruptedException{
        lock.lock();
        try {
            while ( number==0)
                condition2.await();
            number--;
            System.out.println(Thread.currentThread().getName()+" decr->"+number);
            condition3.signal();
        }catch (Exception ex){
            System.err.println("decr wrong");
        }finally{ lock.unlock(); }}public void printC(a)throws InterruptedException{
        lock.lock();
        try {
            while( number! =0)
                condition3.await();
            number++;
            System.out.println(Thread.currentThread().getName()+" incr->"+number);
            condition4.signal();
        }catch (Exception ex){
            System.err.println("incr wrong");
        }finally{ lock.unlock(); }}public void printD(a)throws InterruptedException{
        lock.lock();
        try {
            while ( number==0)
                condition4.await();
            number--;
            System.out.println(Thread.currentThread().getName()+" decr->"+number);
            condition1.signal();
        }catch (Exception ex){
            System.err.println("decr wrong");
        }finally{ lock.unlock(); }}}Copy the code

Running results:

A incr->1
B decr->0
C incr->1
D decr->0
A incr->1
B decr->0
C incr->1
D decr->0
A incr->1
B decr->0
C incr->1
D decr->0
A incr->1
B decr->0
C incr->1
D decr->0
A incr->1
B decr->0
C incr->1
D decr->0
A incr->1
B decr->0
C incr->1
D decr->0
A incr->1
Copy the code