java 并发基础 notify()与notifyAll() 的理解

代码来自java 编程思想第4版 21章

  • 代码1
//: concurrency/NotifyVsNotifyAll.java
package concurrency; /* Added by Eclipse.py */

import java.util.concurrent.*;
import java.util.*;

class Blocker {
    synchronized void waitingCall() {
        try {
            while (!Thread.interrupted()) {
                wait();
                System.out.print(Thread.currentThread() + " ");
            }
        } catch (InterruptedException e) {
            // OK to exit this way
        }
    }

    synchronized void prod() {
        notify();                       
    }

    synchronized void prodAll() {
        notifyAll();
    }
}

class Task implements Runnable {
    static Blocker blocker = new Blocker();

    public void run() {
        blocker.waitingCall();
    }
}

class Task2 implements Runnable {
    // A separate Blocker object:
    static Blocker blocker = new Blocker();

    public void run() {
        blocker.waitingCall();
    }
}

public class NotifyVsNotifyAll {
    public static void main(String[] args) throws Exception {
        ExecutorService exec = Executors.newCachedThreadPool();
        for (int i = 0; i < 5; i++)
            exec.execute(new Task());
        exec.execute(new Task2());
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            boolean prod = true;

            public void run() {
                if (prod) {
                    System.out.print("\nnotify() ");
                    Task.blocker.prod();
                    prod = false;
                } else {
                    System.out.print("\nnotifyAll() ");
                    Task.blocker.prodAll();
                    prod = true;
                }
            }
        }, 400, 400); // Run every .4 second
        TimeUnit.SECONDS.sleep(5); // Run for a while...
        timer.cancel();
        System.out.println("\nTimer canceled");
        TimeUnit.MILLISECONDS.sleep(500);
        System.out.print("Task2.blocker.prodAll() ");
        Task2.blocker.prodAll();
        TimeUnit.MILLISECONDS.sleep(500);
        System.out.println("\nShutting down");
        exec.shutdownNow(); // Interrupt all tasks
    }
} /*
     * Output: (Sample) notify() Thread[pool-1-thread-1,5,main] notifyAll()
     * Thread[pool-1-thread-1,5,main] Thread[pool-1-thread-5,5,main]
     * Thread[pool-1-thread-4,5,main] Thread[pool-1-thread-3,5,main]
     * Thread[pool-1-thread-2,5,main] notify() Thread[pool-1-thread-1,5,main]
     * notifyAll() Thread[pool-1-thread-1,5,main] Thread[pool-1-thread-2,5,main]
     * Thread[pool-1-thread-3,5,main] Thread[pool-1-thread-4,5,main]
     * Thread[pool-1-thread-5,5,main] notify() Thread[pool-1-thread-1,5,main]
     * notifyAll() Thread[pool-1-thread-1,5,main] Thread[pool-1-thread-5,5,main]
     * Thread[pool-1-thread-4,5,main] Thread[pool-1-thread-3,5,main]
     * Thread[pool-1-thread-2,5,main] notify() Thread[pool-1-thread-1,5,main]
     * notifyAll() Thread[pool-1-thread-1,5,main] Thread[pool-1-thread-2,5,main]
     * Thread[pool-1-thread-3,5,main] Thread[pool-1-thread-4,5,main]
     * Thread[pool-1-thread-5,5,main] notify() Thread[pool-1-thread-1,5,main]
     * notifyAll() Thread[pool-1-thread-1,5,main] Thread[pool-1-thread-5,5,main]
     * Thread[pool-1-thread-4,5,main] Thread[pool-1-thread-3,5,main]
     * Thread[pool-1-thread-2,5,main] notify() Thread[pool-1-thread-1,5,main]
     * notifyAll() Thread[pool-1-thread-1,5,main] Thread[pool-1-thread-2,5,main]
     * Thread[pool-1-thread-3,5,main] Thread[pool-1-thread-4,5,main]
     * Thread[pool-1-thread-5,5,main] Timer canceled Task2.blocker.prodAll()
     * Thread[pool-1-thread-6,5,main] Shutting down
     */// :~

  • 代码2

    //: concurrency/Restaurant.java
    package concurrency; /* Added by Eclipse.py */
    
    // The producer-consumer approach to task cooperation.
    
    import java.util.concurrent.*;
    import static net.mindview.util.Print.*;
    
    class Meal {
    private final int orderNum;
    
    public Meal(int orderNum) {
        this.orderNum = orderNum;
    }
    
    public String toString() {
        return "Meal " + orderNum;
    }
    }
    
    class WaitPerson implements Runnable {
    private Restaurant restaurant;
    
    public WaitPerson(Restaurant r) {
        restaurant = r;
    }
    
    public void run() {
        try {
            while (!Thread.interrupted()) {
                synchronized (this) {
                    while (restaurant.meal == null)
                        wait(); // ... for the chef to produce a meal
                }
                print("Waitperson got " + restaurant.meal);
                synchronized (restaurant.chef) {
                    restaurant.meal = null;
                    restaurant.chef.notifyAll(); // Ready for another
                      //这里的notifyAll();///////////////////////
                }
            }
        } catch (InterruptedException e) {
            print("WaitPerson interrupted");
        }
    }
    }
    
    class Chef implements Runnable {
    private Restaurant restaurant;
    private int count = 0;
    
    public Chef(Restaurant r) {
        restaurant = r;
    }
    
    public void run() {
        try {
            while (!Thread.interrupted()) {
                synchronized (this) {
                    while (restaurant.meal != null)  
                        wait(); // ... for the meal to be taken
                }
                if (++count == 10) {
                    print("Out of food, closing");
                    restaurant.exec.shutdownNow();
                }
                printnb("Order up! ");
                synchronized (restaurant.waitPerson) {
                    restaurant.meal = new Meal(count);
                    restaurant.waitPerson.notifyAll();-//////////////
                }
                TimeUnit.MILLISECONDS.sleep(100);
            }
        } catch (InterruptedException e) {
            print("Chef interrupted");
        }
    }
    }
    
    public class Restaurant {
    Meal meal;
    ExecutorService exec = Executors.newCachedThreadPool();
    WaitPerson waitPerson = new WaitPerson(this);
    Chef chef = new Chef(this);
    
    public Restaurant() {
        exec.execute(chef);
        exec.execute(waitPerson);
    }
    
    public static void main(String[] args) {
        new Restaurant();
    }
    } /*
     * Output: Order up! Waitperson got Meal 1 Order up! Waitperson got Meal 2 Order
     * up! Waitperson got Meal 3 Order up! Waitperson got Meal 4 Order up!
     * Waitperson got Meal 5 Order up! Waitperson got Meal 6 Order up! Waitperson
     * got Meal 7 Order up! Waitperson got Meal 8 Order up! Waitperson * got Meal 9
     * Out of food, closing WaitPerson interrupted Order up! Chef interrupted
     */// :~
    
    
  • 问题

    1. notifyAll是Object的方法 ---public final native void notifyAll();但不是静态方法

这个在代码1中,为什么没有实例对象就可以直接调用该方法

而在代码2中为什么需要 restaurant.waitPerson.notifyAll() ;如果直接改成notifyAll(); 会报错

Order up! Exception in thread "pool-1-thread-1" java.lang.IllegalMonitorStateException
    at java.lang.Object.notifyAll(Native Method)
    at concurrency.Chef.run(Restaurant.java:69)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
  1. 这2种调用的方法有什么区别

首先notify、notifyAll 都是唤醒wait等待的线程,能唤醒的前提是,notify、notifyAll必须与wait用的是同一把锁。
比如代码1中Blocker 的prod和prodAll方法锁的就是this。再说区别,比如利用wait、notify、notifyAll 实现生产者、消费者。
使用notify会出现如图片所示的问题。而notifyAll是尝试唤醒所有线程直到阻塞队列中满足执行条件就会执行。
图片说明。再来说这个在代码1中,
为什么没有实例对象就可以直接调用该方法?因为默认是this去调因为锁的是this。而代码二中明确了锁的对象,不能用this,否则阻塞的不是你锁住的锁,java不允许这样。另外wait会释放锁,等到唤醒会重新判断条件。这个是生产者消费者的例子https://blog.csdn.net/leel0330/article/details/80455307。
参考这个并看图片描述,希望对你有帮助。生产者把queue放满,是假设的,即存在这种情况

如果某些线程在等待某些条件触发,那当那些条件为真时,你可以用 notify 和 notifyAll 来通知那些等待中的线程重新开始运行。不同之处在于,notify 仅仅通知一个线程,并且我们不知道哪个线程会收到通知,然而 notifyAll 会通知所有等待中的线程

代码1中的notifyAll()其实就是this.notifyAll(),只是把this省略了,它泛指当前对象,它在你的Task类里被定义成了静态对象,在项目启动时就被实例化了

wait()、notify、notifyAll() 都是 Object 类的方法,它们的调用都必须是基于内置锁对象的,即必须在对应的 Object 内置锁上才能调用该对象的这些方法。
具体表现在都需要在 synchronized 代码块中调用对应内置锁对象的这些方法。
例如:synchronized (restaurant.chef) 这里同步块锁对象是 restaurant.chef ,所以必须调用 restaurant.chef 相关的 notify 或者 notifyAll 方法。