JAVA high concurrency (JUC) producer and consumer

This time we are explaining an example
problem : Title : Two threads operate on a variable to realize that two threads can add 1 to the same resource, and the other to subtract 1, and it needs to be implemented alternately. The initial value of the variable is 0 . That is, two threads perform an alternating operation of plus one minus one on the same resource.
Not much to say,
let's go. First, we first define the resources of the operation and define the methods.

//资源类
class Resource {
    private int number = 0;

    public synchronized void up() throws InterruptedException {
        //1.判断
        if(number != 0) {
            this.wait();
        }
        //2.干活
        number++;
        System.out.println(Thread.currentThread().getName() + "\t" + number);
        this.notifyAll();
    }

    public synchronized void down() throws InterruptedException {
        if(number == 0) {
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName() + "\t" + number);
        this.notifyAll();
    }
}

Then we write our two threads:

public class ThreadWaitNotifyDemo {
    public static void main(String[] args) {
        Resource resource = new Resource();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    resource.up();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    resource.down();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();      
    }
}

The result is as follows:
Insert picture description here
Here we use Lambda expressions to create threads and start them through anonymous inner classes. You can see a thread up method and another thread down method.
First, for example, thread A first judges whether it is 0, not 0. Then add 1 operation at this time. At the same time, the B thread judges that the number is equal to 0, and then waits. At this time, the A thread is added, and then other threads are awakened by the notifyAll() method, so it is completed In addition to the subtraction operation, here for0~10 is to ensure that it can be performed alternately 10 times.
Here is an extension of knowledge . Is the wait method and notify method the Thread method?
Answer: wrong wrong wrong.
Looking at the API, we can see that these two methods belong to Object methods. Wait and notify must be used in conjunction with the synchronized keyword.
Insert picture description here
Insert picture description here
Is this over? ?
No, no, the requirements have changed at this time! At this time, the project manager came over and said, Xiao Wang, I don’t want two threads to operate, I want four threads to operate at the same time, two for adding and two for subtracting, or the alternate time must be 0 and 1 alternately. .
At this time, I thought, simple, I will add two more threads!
Therefore, two more threads are added.
The code is as follows, and the Resource remains unchanged.

public class ThreadWaitNotifyDemo {
    public static void main(String[] args) {
        Resource resource = new Resource();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    resource.up();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    resource.down();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    resource.up();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "C").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                try {
                    resource.down();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "D").start();
    }
}

As before, two more threads are added, one for adding and one for subtracting.
Then run it, and the result is as follows:
Insert picture description here
Here we can see that some are 3, what is the situation? ? ?
Analysis: For example, A and C threads perform addition operations, B and D perform subtraction operations. If it is 0, then B, and D threads are waited, right, A and C are blocked because of the up method. The lock is added, so there is only one method to add. If A finishes the operation at this time, and then notifyall, then the C thread will also perform the up operation at this time, because the C thread is in if (number!=0) {this.wait( );} After being awakened, other operations are continued, and the number is not judged. The same is true for the B and D threads, so the above picture will appear!
So how can this situation be solved? ?
Because we did not re-judgment, then we let it re-judgment is i!
Modify the resource class code as follows:

class Resource {
    private int number = 0;

    public synchronized void up() throws InterruptedException {
        //1.判断
        while (number != 0) {
            this.wait();
        }
        //2.干活
        number++;
        System.out.println(Thread.currentThread().getName() + "\t" + number);
        this.notifyAll();
    }

    public synchronized void down() throws InterruptedException {
        while (number == 0) {
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName() + "\t" + number);
        this.notifyAll();
    }
}

The code for operating resources of the four threads remains unchanged, and the results are as follows:

Insert picture description here
Since our title is JUC , we naturally use JUC to solve this problem:
first read the API:
Lock interface:
Insert picture description here
Insert picture description here
Insert picture description hereCondition interface: Insert picture description here
that is, we used synchronized, wait, notify, and now we use JUC's lock (ReentrantLock), condition , Condition.await(), condition.signalAll(); The
code is as follows:

class Resource {
    private int number = 0;
    private Lock lock = new ReentrantLock();  //可重入锁
    private Condition condition = lock.newCondition();

    public void up() throws InterruptedException{
        lock.lock();
        try {
            while (number != 0) {
                condition.await(); //相当于wait
            }
            number++;
            System.out.println(Thread.currentThread().getName()+"\t"+number);
            condition.signalAll(); //相当于notifyAll
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void down() throws InterruptedException{
        lock.lock();
        try {
            while (number == 0) {
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName()+"\t"+number);
            condition.signalAll();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

The results are as follows:
Insert picture description here
So the question is, why should we use JUC instead?
Insert picture description here
The emergence of new technology will replace things that technology cannot solve!
Notify wakes up any of the three, and notify wakes up all.
Then look at the advantages of JUC.
This time our topic is upgraded:
the calling sequence between multiple threads, to achieve A->B->C:
the startup sequence of the three threads is as follows:
AA prints 5 times, BB prints 10 times, CC prints 15 times, ... continue 10 rounds.
The code is as follows: the
main method:

public class ThreadOrderAccess {
    public static void main(String[] args) {
        ShareResource resource = new ShareResource();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                resource.printf5();
            }
        }, "A").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                resource.printf10();
            }
        }, "B").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                resource.printf15();
            }
        }, "C").start();
    }
}

Resource class:

class ShareResource {
    private int number = 1;// 1:A  2:B  3:C
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();

    //注意标志位的修改和定位
    //A
    public void printf5() {
        lock.lock();
        try {
            //判断
            while (number != 1) {
                condition1.await();
            }
            //干活
            for (int i = 1; i <= 5; i++) {
                System.out.println(Thread.currentThread().getName() + "\t" + i);
            }
            //通知
            number = 2; //此时B、C一直在wait状态中
            condition2.signal(); //精确打击 唤醒B
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    //B
    public void printf10() {
        lock.lock();
        try {
            while (number != 2) {
                condition2.await();
            }
            for (int i = 1; i <= 10; i++) {
                System.out.println(Thread.currentThread().getName() + "\t" + i);
            }
            number = 3;
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    //C
    public void printf15() {
        lock.lock();
        try {
            while (number != 3) {
                condition3.await();
            }
            for (int i = 1; i <= 15; i++) {
                System.out.println(Thread.currentThread().getName() + "\t" + i);
            }
            number = 1;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

}

Note that the wait and other methods hereafter are replaced by JUC and then the java.lang.IllegalMonitorStateException
part of the result is as follows:
Insert picture description here
Insert picture description here
So this is the optimization of the old technology, accurate notification, accurate wake-up, and wake up to the specified condition to achieve the specified The thread wakes up.
This is the explanation for this time. If there is any need for improvement, please leave a message below!

Guess you like

Origin blog.csdn.net/Pzzzz_wwy/article/details/106008088