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 条评论
登录 后参与评论

相关文章

来自专栏编程心路

SSH框架之旅-struts2(1)

struts2 框架在 struts1 和 WebWork的技术上合并而来的,全新的struts2 框架是以 WebWork 框架为核心,采用拦截器的机制来处理...

693
来自专栏技术博客

ExtJs四(ExtJs MVC登录窗口的调试)

继上一节中实现了验证码http://www.cnblogs.com/aehyok/archive/2013/04/19/3030212.html,现在我们可以进...

572
来自专栏信安之路

IAT 三连之什么是 IAT?

IAT 的全称是 ImportAddress Table。在可执行文件中使用其他 DLL 可执行文件的代码或数据,称为导入或者输入,当 PE 文件载入内存时,w...

660
来自专栏kwcode

System.Runtime.InteropServices.COMException (0x800A03EC): 无法访问文件

System.Runtime.InteropServices.COMException (0x800A03EC): 无法访问文件。请尝试下列方法之一:

572
来自专栏我的小碗汤

go语言path/filepath包之Walk源码解析

go语言的path/filepath包包提供了很多兼容各个操作系统的文件路径实用操作方法,今天只来看看Walk方法:

512
来自专栏炉边夜话

JNI使用技巧点滴(二)

作者:normalnotebook 背景<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com...

722
来自专栏kevindroid

NDK学习笔记(1)——第一个jni程序

984
来自专栏简书专栏

基于python的Scrapy爬虫框架实战

命令:scrapy genspider article "blog.jobbole.com" 注意:运行此命令时必须在爬虫工程文件夹内,如下图路径所示。...

424
来自专栏Java 源码分析

SpringBoot 笔记 ( 二 ):自定义配置

SpringBoot 笔记 ( 二 ) 1. 配置文件 SpringBoot使用一个全局的配置文件,配置文件名是固定的: application.propert...

3206
来自专栏自动化测试实战

接口测试基础——第10篇 threading多线程和sys

3325

扫码关注云+社区