桥山之巅,姬水之畔

JAVA多线程-Object.wait(),Object.notify(),Object.notifyAll()

2019.11.18

初步理解

wait()

  1. 使线程停止运行,进入等待队列,会立刻释放对象锁
  2. wait(0) 代表的是无限期的等待,不能自动唤醒。
  3. java.lang.Object类方法。

notify()

  1. 唤醒一个正在等待当前对象的waiting线程,遵循FIFO(先进先出)策略。
  2. notify()调用后,并不是马上就释放对象锁,而是在相应的synchronized语句块执行结束后,自动释放锁后,JVM在wait()对象锁的线程中随机选取一线程赋予其对象锁,唤醒线程,继续执行。这样就提供了在线间同步、唤醒的操作
  3. java.lang.Object类方法。

notifyAll()

  1. 唤醒正在等待当前对象的所有等待线程。
  2. 默认的唤醒策略是:LIFO(后进先出)。
  3. java.lang.Object类方法。

wait()与notify()

  1. Obj.wait(),与Obj.notify()必须与synchronized(Obj)一起使用,也就是wait()是针对已经获取了Obj锁进行操作。
  2. 语法角度来说就是Obj.wait(),Obj.notify()必须在、synchronized(Obj)语句块内。
  3. 功能上来说wait就是指在该线程获取对象锁后,主动释放对象锁,同时本线程休眠。notify()就是对对象锁的唤醒操作。

wait()与sleep() 共同点

  1. 都可以指定线程阻塞的时间。
  2. 都可以通过interrupt()方法打断(不建议使用)线程的暂停状态 ,从而使线程立刻抛出InterruptedException。如果线程A希望立即结束线程B,则可以对线程B对应的Thread实例调用interrupt方法。如果此刻线程B正在wait/sleep /join,则线程B会立刻抛出InterruptedException,在catch() {} 中直接return即可安全地结束线程。 需要注意的是,InterruptedException是线程自己从内部抛出的,并不是interrupt()方法抛出的。对某一线程调用 interrupt()时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptedException。但是,一旦该线程进入到 wait()/sleep()/join()后,就会立刻抛出InterruptedException 。

不同点

  1. sleep()是Thread类的方法,wait()是Object类的方法。
  2. sleep()方法没有释放锁,而wait()方法释放了锁。
  3. wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用。

执行notify()后,waiting线程可能的状态

线程a先抢到了对象o的锁,然后wait,然后b抢到了o的锁,然后b中调用o.notify并释放锁,此时a是running状态还是blocked状态?? 如果b在执行完notify()后没有释放锁则线程a是阻塞等待, 如果线程b执行完同步代码块(释放锁)后,则线程a就是就绪态,不一定是运行态

代码实操,三线程打印ABC

建立三个线程,A线程打印10次A,B线程打印10次B,C线程打印10次C,要求线程同时运行,交替打印10次ABC。这个问题用Object的wait(),notify()就可以很方便的解决。

public class T08WaitNotify {

    class T implements Runnable{
        // 自定义线程名称
        private String name;
        // 前一个obj
        private Object prev;
        // 当前obj
        private Object self;

        T(String name, Object prev, Object self){
            this.name = name;
            this.prev = prev;
            this.self = self;
        }

        @Override
        public void run() {
            int count = 0;
            while (count < 10) {
                synchronized (prev) {
                    synchronized (self) {
                        System.out.print(name);
                        count++;
                        self.notify();
                    }
                    try {
                        prev.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            }
        }
    }

    public static void main(String[] args) throws InterruptedException {

        T08WaitNotify obj = new T08WaitNotify();

        Object oa = new Object();
        Object ob = new Object();
        Object oc = new Object();

        T a = obj.new T("A", oc, oa);
        T b = obj.new T("B", oa, ob);
        T c = obj.new T("C", ob, oc);

        Thread ta = new Thread(a);
        Thread tb = new Thread(b);
        Thread tc = new Thread(c);

        ta.start();
        Thread.sleep(1000); // 确保按照A、B、C顺序执行
        tb.start();
        Thread.sleep(1000);
        tc.start();
        Thread.sleep(1000);
    }
}

代码流程说明 A线程启动,依次取得c、a锁,打印A,执行notify()唤醒任意一个a对象等待池中的线程,待synchronized(self){}块执行完后释放a锁,执行wait(),将A线程放入c对象相关的等待池中等待被唤醒同时释放c锁; 1秒后,B线程启动,依次取得a、b锁,打印B,执行notify()唤醒任意一个b对象等待池中的线程,待synchronized(self){}块执行完后释放b锁,执行wait(),将B线程放入a对象相关的等待池中等待被唤醒,同时释放a锁;1秒后,C线程启动,依次取得b、c锁,打印C,执行notify()唤醒任意一个c对象等待池中的线程(此时A线程被唤醒),待synchronized(self){}块执行完后释放c锁,执行wait(),将C线程放入b对象相关的等待池中等待被唤醒,同时释放b锁。

至此,完成一次ABC的打印。继续执行,从A线程开始,执行各自线程中的下一次循环。

特别说明 Java源码中wait()默认调用的是wait(0),表明线程无限等待,只能被其他线程唤醒。

参考 https://blog.csdn.net/u010002184/article/details/82912225 https://blog.csdn.net/hj1997a/article/details/84284973