栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

wait,notify/notifyAll的使用及实现原理

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

wait,notify/notifyAll的使用及实现原理

1、介绍

java中的wait/notify的等待唤醒机制可以实现线程间的通信,wait使当前线程进入等待,该操作会使线程阻塞,直到其它线程唤醒(调用notify、notifyAll方法)它才可继续执行。

  • wait()、notify()/notifyAll()都是定义在 Object类的final方法,不支持重写。
  • wait()、notify()/notifyAll()都和对象监视器密切相关,所以这几个方法都需和synchronized搭配使用。
  • wait()方法在已获取锁的前提下,使当前线程阻塞,释放当前锁,等待其它线程唤醒。
  • notify()唤醒此对象监视器上阻塞等待的单个线程,不释放锁,被唤醒的线程参与到锁的竞争中。
  • notifyAll()唤醒此对象监视器上阻塞等待的所有线程,不释放锁,被唤醒的所有线程参与到锁的竞争中。
2、使用案例 案例壹:使用两个线程交替打印1-26,a-z
public class Demo {

    public synchronized void printNum() {
        for(int i=1;i<=26;i++){
            this.notify();
            System.out.print(i);
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public synchronized void printAz(){
        char[] arr = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
        for(int i=0;i
            this.notify();
            System.out.print(arr[i]);
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    public static void main(String[] args) {
        final Demo demo = new Demo();
        new Thread(()->{demo.printNum();}).start();
        new Thread(()->{demo.printAz();}).start();
    }
}


1a2b3c4d5e6f7g8h9i10j11k12l13m14n15o16p17q18r19s20t21u22v23w24x25y26z
案例贰:使用三个线程交替打印1-26,a-z,A-Z
public class Demo {
    public void printNum() {
        for(int i=1;i<=26;i++){
            synchronized ("AZ"){
                synchronized ("num"){
                    System.out.print(i);
                    "num".notify();
                }
                try {
                    "AZ".wait();
                } catch (InterruptedException e) { e.printStackTrace(); }
            }
        }
    }

    public void printAz(){
        char[] arr = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
        for(int i=0;i
            synchronized ("num"){
                synchronized ("az"){
                    System.out.print(arr[i]);
                    "az".notify();
                }
                try {
                    "num".wait();
                } catch (InterruptedException e) { e.printStackTrace(); }
            }
        }
    }

    public void printAZ(){
        char[] arr = {'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'};
        for(int i=0;i
            synchronized ("az"){
                synchronized ("AZ"){
                    System.out.print(arr[i]);
                    "AZ".notify();
                }
                try {
                    "az".wait();
                } catch (InterruptedException e) { e.printStackTrace(); }
            }
        }
    }


    public static void main(String[] args) throws InterruptedException {
        Demo demo = new Demo();
        
        new Thread(()->{demo.printNum();}).start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{demo.printAz();}).start();
        TimeUnit.SECONDS.sleep(1);
        new Thread(()->{demo.printAZ();}).start();
    }
}


1aA2bB3cC4dD5eE6fF7gG8hH9iI10jJ11kK12lL13mM14nN15oO16pP17qQ18rR19sS20tT21uU22vV23wW24xX25yY26zZ
3、实现原理

java线程的五种状态图解:

重量级锁通过对象内部的监视器(monitor)实现,监视器内部记录了获取当前监视器的线程和进入锁的次数等信息。monitor是由ObjectMonitor实现的,其主要数据结构如下(位于HotSpot虚拟机源码ObjectMonitor.hpp文件,C++实现的),内部记录了处于等待blockd状态的线程列表和处于锁定blockd状态的线程列表。

ObjectMonitor() {
    _header       = NULL;
    _count        = 0; 
    _waiters      = 0,
    _recursions   = 0;
    _object       = NULL;
    _owner        = NULL;
    _WaitSet      = NULL; //处于等待blockd状态的线程,会被加入到_WaitSet
    _WaitSetLock  = 0 ;
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ;//处于锁定blockd状态的线程,会被加入到_cxq
    FreeNext      = NULL ;
    _EntryList    = NULL ; 
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
  }

monitor获取释放图解:

加入wait、notify后monitor获取释放图解:

4、注意事项

根据monitor获取释放图解,锁对象调用wait、notify/notifyAll前提是当前线程已经获取到锁对象。

若当前线程获取到a对象锁,使用b对象调用wait、notify/notifyAll,则会报运行时异常。

演示1(正常)
public class Demo {
    public static void main(String[] args) throws InterruptedException {
        new Thread(()->{
            synchronized ("lock1"){
                try {
                	//当前线程已获取到字符串lock1的锁对象,可以释放。
                    "lock1".wait();
                    System.out.println("lock1锁住的线程已被唤醒");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        TimeUnit.SECONDS.sleep(1);
        synchronized ("lock1"){
        	//当前线程已获取到字符串对象lock1的锁对象,可以唤醒。
            "lock1".notify();
        }
    }
}
演示2(正常)
public class Demo {
    public static void main(String[] args) throws InterruptedException {
        new Thread(()->{
            synchronized ("lock1"){
                synchronized ("lock2"){
                    try {
                    	当前线程已获取到字符串lock1、lock2的锁对象,lock1可以释放。
                        "lock1".wait();
                        System.out.println("lock1锁住的线程已被唤醒");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
        TimeUnit.SECONDS.sleep(1);
        synchronized ("lock1"){
        	//当前线程已获取到字符串对象lock1的锁对象,可以唤醒。
            "lock1".notify();
        }
    }
}
演示3(异常)
public class Demo {
    public static void main(String[] args){
        new Thread(()->{
            synchronized ("lock1"){
                try {
                	//当前线程已获取到字符串对象lock1的锁对象,释放lock2异常
                    TimeUnit.SECONDS.sleep(1);
                    "lock2".wait();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
        new Thread(()->{
            synchronized ("lock2"){
                try {
                    TimeUnit.SECONDS.sleep(1);
                    //当前线程已获取到字符串对象lock2的锁对象,释放lock1异常
                    "lock1".wait();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}


java.lang.IllegalMonitorStateException
java.lang.IllegalMonitorStateException
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/826781.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号