In this article, we use wait(), notify(), and notifyAll() to simulate producer-consumer examples to explain why deadlock occurs when notify() is used.

1. Code examples

1.1 producers

package com.example.hxk.thread.demo;

import java.util.List;
import java.util.concurrent.TimeUnit;

/ * * *@author Smith 2019/3/21
 */
public class Producer implements Runnable{

    List<Integer> cache;

    public void put(a) throws InterruptedException {
        synchronized (cache) {
            while (cache.size() == 1) {
                try {
                    cache.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }

            TimeUnit.SECONDS.sleep(1);

            cache.add(1);
            System.out.println(Thread.currentThread().getName() + "The producer produces one."); cache.notify(); }}public Producer(List<Integer> cache) {
        this.cache = cache;
    }

    @Override
    public void run(a) {
        while (true) {
            try {
                put();
            } catch(InterruptedException e) { e.printStackTrace(); }}}}Copy the code

1.2 consumers

package com.example.hxk.thread.demo;

import java.util.List;

/ * * *@author Smith 2019/3/21
 */
public class Customer implements Runnable {

    List<Integer> cache;

    public Customer(List<Integer> cache) {
        this.cache = cache;
    }

    private void custom(a) {
        synchronized (cache) {
            while (cache.size() == 0) {
                try {
                    cache.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            cache.remove(0);
            System.out.println(Thread.currentThread().getName() + "The consumer consumed one."); cache.notify(); }}@Override
    public void run(a) {
        while (true) { custom(); }}}Copy the code

1.3 Test Code

1.3.1 One producer one consumer

package com.example.hxk.thread.demo;

import java.util.ArrayList;
import java.util.List;

/ * * *@author Smith 2019/3/21
 */
public class Test {

    public static void main(String[] args) {

        List<Integer> cache = new ArrayList<>();
        new Thread(new Producer(cache), "P1").start();
        new Thread(new Customer(cache), "C1").start(); }}Copy the code

Running results:

Conclusion:

With Notify, one producer, one consumer, production and consumption run smoothly without any problems.

1.3.2 One producer two consumers

package com.example.hxk.thread.demo;

import java.util.ArrayList;
import java.util.List;

/ * * *@author Smith 2019/3/21
 */
public class Test {

    public static void main(String[] args) {

        List<Integer> cache = new ArrayList<>();

        new Thread(new Producer(cache), "P1").start();
        new Thread(new Customer(cache), "C1").start();
        new Thread(new Customer(cache), "C2").start(); }}Copy the code

Running results:

Conclusion:

With notify and one producer and two consumers, the program deadlocks after producing twice.

1.3.3 Replace notify() in Producer and Customer with notifyAll()

The code is not sticky.

The results

The program is back to normal.

2. The difference between notify() and notifyAll

Each synchronization object has its own lock pool and wait pool.

2.1 Lock Pool and Wait Pool

  • Lock pool: Suppose thread A already owns the lock on an object (not A class), and other threads want to call A synchronized method (or block) of that object. Since these threads must acquire ownership of the lock before they can access the synchronized method of the object, However, the lock on this object is currently owned by thread A, so these threads enter the lock pool on this object.
  • Waiting for the pool: If thread A calls the wait() method of an object, thread A releases the lock on that object (since A wait() must occur in synchronized, thus owning the lock before executing its wait()), and thread A enters the object’s wait pool. If another thread calls notifyAll() on the same object, all the threads in the object’s wait pool enter the lock pool, ready to compete for ownership of the lock. If another thread calls notify() on the same object, only one thread in the wait pool of that object (randomly) will enter the lock pool of that object.

2.2 The difference between notify() and notifyAll()

  • A thread that calls wait() releases the lock and enters the wait pool without competing for the lock
  • After notify() is called, a thread (only one thread) in the waiting pool will enter the lock pool of the object and compete for the lock. If the competition succeeds, the thread will acquire the lock. If the competition fails, the thread will remain in the lock pool and wait for the next lock competition.
  • When notifyAll() is called, all threads in the wait pool join the lock pool for the object.

3

Now that we know about 2, the three examples above are easy to explain.

1.3.1: Because there is only one producer and consumer, there is always only one thread in the waiting pool. Either the producer wakes up the consumer, or the consumer wakes up the producer, so the program can run successfully.

1.3.2: Here’s a possible example

  1. Now there are three threads, producer P1, consumer C1, and C2. At the start of the run, all three threads are waiting in the lock pool for contention. Suppose C1 grabs the lock, and C1 calls wait() to release the lock and enter the wait pool because there are no resources to consume.
  2. C2 grabs the lock, starts to consume, and similarly C2 enters the waiting pool. Now we only have P1 left in the lock pool.
  3. After production, P1 calls notify() to wake up C1 or C2 in the wait pool. Then P1 calls wait() to release the lock and enter the wait pool.
  4. Let’s say C1 is awakened, C1 goes into the lock pool and obtains the lock. After consumption, notify() wakes up C2,C2 goes into the lock pool, C1 goes into the wait pool, and now there is only C1 in the lock pool.
  5. C1 acquires the lock, finds no resources to consume, waits () releases the lock, and enters the wait pool. Now all three threads are in the wait pool, and there are no threads in the lock pool. Cause deadlock!

1.3.3: notifyAll() does not wake up only the same type of thread, so the deadlock of 1.3.2 does not occur.

reference

  • Author: emailed

Source: the original CSDN: blog.csdn.net/emailed/art…

  • By: Puff Snatcher

Source: Jane books Original: www.jianshu.com/p/45626f4e0…