Java的wait()、notify()学习三部曲之二:修改JVM源码看参数

在上一章《 Java的wait()、notify()学习三部曲之一:JVM源码分析》中,我们通过JVM源码分析了线程同步的相关操作,但还是留下了一些疑惑未解:在notify()和释放锁的时候,对等待锁的线程的处理有多个分支,具体走到哪个分支视Policy和QMode的值而定,今天我们实战一次,修改JVM源码将这两个参数在虚拟机运行的时候打印出来看看;

编译JVM源码需要搭建编译环境,推荐使用docker,因为我已准备好了一个完善的编译环境镜像,详情请参照《极速体验编译openjdk8(docker环境》)

如果您用的是linxu或Mac操作系统,那么可直接安装官方docker软件; 如果您用的是win10专业版,也能直接安装官方docker软件; 如果是win10家庭版是无法安装docker的,这时可以装vmware,再安装linux系统,推荐centos7,再在centos7上安装docker,参考[《极速体验编译openjdk8(docker环境) 》](http://blog.csdn.net/boling_cavalry/article/details/77623193 );

在docker上启动了bolingcavalry/bolingcavalryopenjdk:0.0.1镜像后: a. 执行docker exec -it compilejdk/bin/bash进入容器; b. 执行vi /usr/local/openjdk/hotspot/src/share/vm/runtime/objectMonitor.cpp打开要修改的文件; c. 找到方法void ObjectMonitor::notify(TRAPS),找到方法内代码:“int Policy = Knob_MoveNotifyee ;”,在下面新增一行“printf(“*** Policy : %d\n”, Policy);”,如下图:

d. 找到方法void ATTR ObjectMonitor::exit(bool not_suspended, TRAPS),找到方法内代码”int QMode = Knob_QMode ;”,在下面新增一行“printf(“*** QMode : %d\n”, QMode);”,如下图:

e. 在/usl/local/openjdk/目录下执行./configure –with-debug-level=slowdebug完成配置检查; f. 在/usl/local/openjdk/目录下执行bluemake all ZIP_DEBUGINFO_FILES=0 DISABLE_HOTSPOT_OS_VERSION_CHECK=OK CONF=linux-x86_64-normal-server-slowdebug开始编译,大约20分钟,编译完成,如下图:

g. 编译完成后进入目录,创建文件NotifyDemo.java文件,内容如下:

public class NotifyDemo {

    private static void sleep(long sleepVal){
        try{
            Thread.sleep(sleepVal);
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    private static void log(String desc){
        System.out.println(Thread.currentThread().getName() + " : " + desc);
    }

    Object lock = new Object();

    public void startThreadA(){
        new Thread(() -> {
            synchronized (lock){
                log("get lock");
                startThreadB();
                log("start wait");
                try {
                    lock.wait();
                }catch(InterruptedException e){
                    e.printStackTrace();
                }

                log("get lock after wait");
                log("release lock");
            }
        }, "thread-A").start();
    }

    public void startThreadB(){
        new Thread(()->{
            synchronized (lock){
                log("get lock");
                startThreadC();
                sleep(100);
                log("start notify");
                lock.notify();
                log("release lock");

            }
        },"thread-B").start();
    }

    public void startThreadC(){
        new Thread(() -> {
            synchronized (lock){
                log("get lock");
                log("release lock");
            }
        }, "thread-C").start();
    }

    public static void main(String[] args){
        new NotifyDemo().startThreadA();
    }
}

h. 在此目录下执行./javac NotifyDemo.java编译源码; i. 在此目录下执行./java NotifyDemo执行class,可以看到输出如下图:

如上图所示,已将运行时的Policy和QMode打印出来,我们来分析一下吧:

首先,Policy=2,表示线程A从等待队列_WaitSet中被取出,又因为_EntryList为空,所以A放入了_EntryList首位,BlOCKING状态的线程C在_cxq,所以A和C放在不同的队列中:

其次,QMode=0,在ObjectMonitor::exit方法中,对QMode等于1、2、3、4的时候都有特殊处理(例如从_EntryList中取出数据),但是对QMode等于0没有特殊处理,而是依次从_EntryList中取出线程来唤醒,如下图,由于A放在_EntryList中,所以A总是先唤醒;

通过日志确定参数值,在结合代码分析,我们把上一章的遗留问题已经搞清楚了,在下一章中,我们会继续修改源码,操控线程A和C对锁的抢占顺序。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏前端儿

深入理解JavaScript的事件循环(Event Loop)

在两个环境下的Event Loop实现是不一样的,在浏览器中基于 规范 来实现,不同浏览器可能有小小区别。在Node中基于 libuv 这个库来实现

612
来自专栏北京马哥教育

Tornado 源码阅读:初步认识

而`ioloop`的核心部分则是 `while True`这个循环内部的逻辑,贴上他的代码下

692
来自专栏编码小白

tomcat源码解读四 tomcat中的processer

     Processor是一个接口,针对于不同协议下具有不同的具体实现类,其实现类的具体功能是处理http请求,主要是对协议进行解析,状态处理以及响应。然后...

4167
来自专栏惨绿少年

Shell编程基础篇-下

1.1 条件表达式 1.1.1 文件判断 常用文件测试操作符 常用文件测试操作符 说明 -d文件,d的全拼为directory 文...

1810
来自专栏iOS 开发杂谈

浅谈 iOS NSNotification

NSNotificationCenter 就相当于一个广播站,使用 [NSNotificationCenter defaultCenter] 来获取,NSNot...

643
来自专栏卡少编程之旅

js线程机制的介绍和练习

31513
来自专栏飞雪无情的博客

Android中关于线程使用的几点注意事项

我们都知道Android是单线程模型,这意味着Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行,所以你单纯的new一个Thrad并且st...

653
来自专栏刘望舒

Android解析WindowManagerService(一)WMS的诞生

前言 此前我用多篇文章介绍了WindowManager,这个系列我们来介绍WindowManager的管理者WMS,首先我们先来学习WMS是如何产生的。本文源码...

2876
来自专栏walterlv - 吕毅的博客

如何实现一个可以用 await 异步等待的 Awaiter

发布于 2017-10-29 08:38 更新于 2017-10...

542
来自专栏老马说编程

(80) 定时任务的那些坑 / 计算机程序的思维逻辑

本节探讨定时任务,定时任务的应用场景是非常多的,比如: 闹钟程序或任务提醒,指定时间叫床或在指定日期提醒还信用卡 监控系统,每隔一段时间采集下系统数据,对异...

2369

扫码关注云+社区