我的编程空间,编程开发者的网络收藏夹
学习永远不晚

java的Object里wait()实现原理讲解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

java的Object里wait()实现原理讲解

Object中的wait()实现原理

在进行wait()之前,就代表着需要争夺Synchorized,而Synchronized代码块通过javap生成的字节码中包含monitorenter和monitorexit两个指令。

当在进加锁的时候会执行monitorenter指令,执行该指令可以获取对象的monitor。同时在执行Lock.wait()的时候也必须持有monitor对象。

在多核环境下,多个线程有可能同时执行monitorenter指令,并获取lock对象关联的monitor,但只有一个线程可以和monitor建立关联,这个线程执行到wait方法时,wait方法会将当前线程放入wait set,使其进行等待直到被唤醒,并放弃lock对象上的所有同步声明,意味着该线程释放了锁,其他线程可以重新执行加锁操作,notify方法会选择wait set中任意一个线程进行唤醒,notifyAll方法会唤醒monitor的wait set中所有线程。执行完notify方法并不会立马唤醒等待线程。那么wait具体是怎么实现的呢?

首先在HotSpot虚拟机中,monitor采用ObjectMonitor实现,每个线程都具有两个队列,分别为free和used,用来存放ObjectMonitor。如果当前free列表为空,线程将向全局global list请求分配ObjectMonitor。

ObjectMonitor对象中有两个队列,都用来保存ObjectWaiter对象,分别是_WaitSet 和 _EntrySet。_owner用来指向获得ObjectMonitor对象的线程

ObjectWaiter对象是双向链表结构,保存了_thread(当前线程)以及当前的状态TState等数据, 每个等待锁的线程都会被封装成ObjectWaiter对象。

  • _WaitSet:处于wait状态的线程,会被加入到wait set;
  • _EntrySett:处于等待锁block状态的线程,会被加入到entry set;

wait方法实现

lock.wait()方法最终通过ObjectMonitor的 wait(jlong millis, bool interruptable, TRAPS)实现

1、将当前线程封装成ObjectWaiter对象node

2、通过ObjectMonitor::AddWaiter方法将node添加到_WaitSet列表中

3、通过ObjectMonitor::exit方法释放当前的ObjectMonitor对象,这样其它竞争线程就可以获取该ObjectMonitor对象

4、最终底层的park方法会挂起线程

ObjectSynchorizer::wait方法通过Object对象找到ObjectMonitor对象来调用方法 ObjectMonitor::wait(),通过调用ObjectMonitor::AddWaiter()可以把新建的ObjectWaiter对象,放入到_WaitSet队列的末尾,然后在ObjectMonitor::exit释放锁,接着通过执行thread_ParkEvent->park来挂起线程,也就是进行wait。

Object对象中的wait,notify,notifyAll的理解

wait,notify,notifyAll 是定义在Object类的实例方法,用于控制线程状态,在线程协作时,大家都会用到notify()或者notifyAll()方法,其中wait与notify是java同步机制中重要的组成部分,需要结合与synchronized关键字才能使用,在调用一个Object的wait与notify/notifyAll的时候,必须保证调用代码对该Object是同步的,也就是说必须在作用等同于synchronized(object){......}的内部才能够去调用obj的wait与notify/notifyAll三个方法,否则就会报错:java.lang.IllegalMonitorStateException:current thread not owner(意思是因为没有同步,所以线程对对象锁的状态是不确定的,不能调用这些方法)。

wait的目的就在于暴露出对象锁,所以需要保证在lock的同步代码中调用lock.wait()方法,让其他线程可以通过对象的notify叫醒等待在该对象的等该池里的线程。同样notify也会释放对象锁,在调用之前必须获得对象的锁,不然也会报异常。所以,在线程自动释放其占有的对象锁后,不会去申请对象锁,只有当线程被唤醒的时候或者达到最大的睡眠时间,它才再次争取对象锁的权利

主要方法:

(1).wait()

等待对象的同步锁,需要获得该对象的同步锁才可以调用这个方法,否则编译可以通过,但运行时会收到一个异常:IllegalMonitorStateException。调用任意对象的 wait() 方法导致该线程阻塞,该线程不可继续执行,并且该对象上的锁被释放。

(2).notify()

唤醒在等待该对象同步锁的线程(只唤醒一个,如果有多个在等待),注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。调用任意对象的notify()方法则导致因调用该对象的 wait()方法而阻塞的线程中随机选择的一个解除阻塞(但要等到获得锁后才真正可执行)。

(3).notifyAll()

唤醒所有等待的线程,注意唤醒的是notify之前wait的线程,对于notify之后的wait线程是没有效果的。

通过一个实例来看一下实际的效果,开启两个线和,一个线程 打印1到52的数字,一个打印A到Z的字母,要求,打印两个数,打印一个字母,这样交替顺序打印,代码如下:



public class ShuZiZiMuThread {
    public static void main(String[] args) {
        Object object = new Object();
        shuzi shuzi = new shuzi(object);
        zimu zimu = new zimu(object);
        Thread t = new Thread(shuzi);
        t.setName("shuzi");
        Thread t1 = new Thread(zimu);
        t1.setName("zimu");
        t.start();//数字线程先运行
        t1.start();
    }
}
class shuzi implements Runnable{
    private Object object;
    //声明类的引用
    public shuzi(Object object) {
        this.object = object;
    }
    public void run() {
        synchronized (object) {//上锁
            for(int i=1;i<53;i++){
                System.out.print(i+",");
                if(i%2==0){
                    object.notifyAll();//唤醒其它争夺权限的线程
                    try {
                        object.wait();//释放锁,进入等待
                        System.out.println("数字打印类打全打印当前对象拥有对象锁的线程"+Thread.currentThread().getName());//输出当前拥有锁的线程名称
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}
class zimu implements Runnable{
    private Object object;
    public zimu(Object object) {
        this.object = object;
    }
    public void run() {
        synchronized (object) {
            for(int j=65;j<91;j++){
                char c = (char)j;
                System.out.print(c);
                object.notifyAll();//唤醒其它争夺权限的线程
                try {
                    object.wait();//释放锁,进入等待
                    System.out.println("字母打印类打全打印当前对象拥有对象锁的线程"+Thread.currentThread().getName());//输出当前拥有锁的线程名称
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

实际运行的结果 :

1,2,A数字打印类打印当前对象拥有对象锁的线程shuzi
3,4,字母打印类打印当前对象拥有对象锁的线程zimu
B数字打印类打印当前对象拥有对象锁的线程shuzi
5,6,字母打印类打印当前对象拥有对象锁的线程zimu
C数字打印类打印当前对象拥有对象锁的线程shuzi
7,8,字母打印类打印当前对象拥有对象锁的线程zimu
D数字打印类打印当前对象拥有对象锁的线程shuzi
9,10,字母打印类打印当前对象拥有对象锁的线程zimu
E数字打印类打印当前对象拥有对象锁的线程shuzi
11,12,字母打印类打印当前对象拥有对象锁的线程zimu
F数字打印类打印当前对象拥有对象锁的线程shuzi
13,14,字母打印类打印当前对象拥有对象锁的线程zimu
G数字打印类打印当前对象拥有对象锁的线程shuzi
15,16,字母打印类打印当前对象拥有对象锁的线程zimu
H数字打印类打印当前对象拥有对象锁的线程shuzi
17,18,字母打印类打印当前对象拥有对象锁的线程zimu
I数字打印类打印当前对象拥有对象锁的线程shuzi
19,20,字母打印类打印当前对象拥有对象锁的线程zimu
J数字打印类打印当前对象拥有对象锁的线程shuzi
21,22,字母打印类打印当前对象拥有对象锁的线程zimu
K数字打印类打印当前对象拥有对象锁的线程shuzi
23,24,字母打印类打印当前对象拥有对象锁的线程zimu
L数字打印类打印当前对象拥有对象锁的线程shuzi
25,26,字母打印类打印当前对象拥有对象锁的线程zimu
M数字打印类打印当前对象拥有对象锁的线程shuzi
27,28,字母打印类打印当前对象拥有对象锁的线程zimu
N数字打印类打印当前对象拥有对象锁的线程shuzi
29,30,字母打印类打印当前对象拥有对象锁的线程zimu
O数字打印类打印当前对象拥有对象锁的线程shuzi
31,32,字母打印类打印当前对象拥有对象锁的线程zimu
P数字打印类打印当前对象拥有对象锁的线程shuzi
33,34,字母打印类打印当前对象拥有对象锁的线程zimu
Q数字打印类打印当前对象拥有对象锁的线程shuzi
35,36,字母打印类打印当前对象拥有对象锁的线程zimu
R数字打印类打印当前对象拥有对象锁的线程shuzi
37,38,字母打印类打印当前对象拥有对象锁的线程zimu
S数字打印类打印当前对象拥有对象锁的线程shuzi
39,40,字母打印类打印当前对象拥有对象锁的线程zimu
T数字打印类打印当前对象拥有对象锁的线程shuzi
41,42,字母打印类打印当前对象拥有对象锁的线程zimu
U数字打印类打印当前对象拥有对象锁的线程shuzi
43,44,字母打印类打印当前对象拥有对象锁的线程zimu
V数字打印类打印当前对象拥有对象锁的线程shuzi
45,46,字母打印类打印当前对象拥有对象锁的线程zimu
W数字打印类打印当前对象拥有对象锁的线程shuzi
47,48,字母打印类打印当前对象拥有对象锁的线程zimu
X数字打印类打印当前对象拥有对象锁的线程shuzi
49,50,字母打印类打印当前对象拥有对象锁的线程zimu
Y数字打印类打印当前对象拥有对象锁的线程shuzi
51,52,字母打印类打印当前对象拥有对象锁的线程zimu
Z数字打印类打印当前对象拥有对象锁的线程shuzi

结果分析:

通过结果可以看出:

在字母打一打印类里 调用完


object.notifyAll();//唤醒其它争夺权限的线程
object.wait();//释放锁,进入等待后,拥有对象锁的线程是shuzi在数字打印类里 调用完
object.notifyAll();//唤醒其它争夺权限的线程
object.wait();//释放锁,进入等待后,拥有对象锁的线程是zimu

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

java的Object里wait()实现原理讲解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

JUC中的wait与notify方法实现原理详解

这篇文章主要介绍了JUC中的wait与notify方法实现原理,在进行wait()之前,就代表着需要争夺Synchorized,而Synchronized代码块通过javap生成的字节码中包含monitor enter和monitor exit两个指令
2023-03-10

Vuecomputed实现原理深入讲解

computed又被称作计算属性,用于动态的根据某个值或某些值的变化,来产生对应的变化,computed具有缓存性,当无关值变化时,不会引起computed声明值的变化。产生一个新的变量并挂载到vue实例上去
2022-11-13

JUC中wait与notify方法的实现原理是什么

今天小编给大家分享一下JUC中wait与notify方法的实现原理是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。1.O
2023-07-05

Web端扫码登录的原理和实现讲解

这篇文章主要介绍了Web端扫码登录的原理和实现,扫码登录相比较传统的密码登录更加快捷和安全,今天我们就来讲讲原理与实现,主要分为几种不同的解决方案,需要的朋友可以参考下
2023-03-23

Android刮刮卡实现原理与代码讲解

实现刮刮卡我们可以Get到哪些技能? * 圆形圆角图片的实现原理 * 双缓冲技术绘图 * Bitmap获取像素值数据 * 获取绘制文本的长宽 * 自定义View的掌握 * 获取屏幕密度 * TypeValue.applyDemension
2022-06-06

java LockSupport实现原理示例解析

这篇文章主要为大家介绍了java LockSupport实现原理示例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-01-09

Java线程的停止实现原理详解

这篇文章主要介绍了Java线程的停止实现原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
2023-01-28

Vue中响应式系统实现原理图文讲解

Vue的响应式实现是借助Object.defineProperty通过重写getter和setter方法来进行的数据劫持,Vue3通过Proxy代理拦截对象中任意属性的变化,通过Reflect反射对源对象的属性进行操作,然后再在get里收集依赖在set里派发更新
2023-03-20

java Semaphore共享锁实现原理解析

这篇文章主要为大家介绍了Semaphore共享锁实现原理解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-01-09

详解Java中跳跃表的原理和实现

跳跃表(Skiplist)是有序链表的扩展,简称跳表,它在原有的有序链表上增加了多级索引,通过索引来实现快速查找,实质上是一种可以进行二分查找的有序链表。本文主要为大家介绍了跳跃表的原理和实现,需要的可以参考一下
2022-12-27

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录