(实验)Java一个线程用synchronized嵌套锁多个对象时调用wait()只释放wait函数关联的所对象还是释放所有锁对象
来源:互联网 发布:阿里云购买云服务器 编辑:程序博客网 时间:2024/06/02 01:17
实验是在JDK1.8下做的。
题目起的比较拗口,其实用代码说明起来更简单,如下所示:
public class MultiSynchronizedTest { private static Object lock1 = new Object(); private static Object lock2 = new Object(); private static class Task1 implements Runnable { @Override public void run() { synchronized (lock1) { synchronized (lock2) { try { lock1.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }}
当一个线程运行Task1时,通过synchronized顺序获得了lock1和lock2的锁,然后在最里层调用锁(lock1或者lock2,下面以lock1为例)的wait()函数,然后按照教科书式的说法,线程进入waiting状态,释放锁,等别的线程调用notify()或者notifyAll()来再次唤醒到runnable状态。那么问题来了,释放锁是一个笼统的说法,到底是只释放wait()函数关联的对象锁(即lock1)还是释放线程当时持有的所有锁(即lock1和lock2)。
直观上来讲,我只调用了lock1.wait()函数,当然只释放lock1。而且我调用哪个对象的wait()就只释放哪个对象的锁,这样程序也更可控。在这里提前先告诉大家,经过实验,结果确实是上面讲的那样:一个线程通过synchronized嵌套锁住多个对象,然后在最里层调用wait()函数,只释放wait()函数关联的锁对象,而不是释放线程当时持有的全部锁。
但是我们也可以直观说线程调用锁对象的wait()函数时,就是释放线程当时持有的所有锁嘛——要不通过wait()自身某种回调机制来释放,或者JVM使得线程进入waiting(或者time_waiting)状态时会统一把持有的锁都释放了。但是直观归直观,信息科学技术(拔的太高了?IT码活)永远是个实践出真知的领域,Object.wait()是个native函数,看明白原理要看cpp源码,JVM就更不用说了,在不研究源码的前提下,做个实验室最方便了。
下面我们用两把全局锁,两个线程和jstack、jps工具来验证下。完整的代码如下:
package com.jxshen.example.jdk.lock;public class MultiSynchronizedTest { private static Object lock1 = new Object(); private static Object lock2 = new Object(); public static void main(String[] args) { Runnable task1 = new Task1(); Thread thread1 = new Thread(task1, "task1"); thread1.start(); Runnable task2 = new Task2(); Thread thread2 = new Thread(task2, "task2"); thread2.start(); } private static class Task1 implements Runnable { @Override public void run() { synchronized (lock1) { System.out.println("Task1 obtain lock1"); synchronized (lock2) { System.out.println("Task1 obtain lock2"); try { lock1.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } private static class Task2 implements Runnable { @Override public void run() { try { Thread.sleep(2000L); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lock2) { System.out.println("Task2 obtain lock2"); } } }}
lock1和lock2是两个静态全局锁,线程task1(代码里我把线程名和任务名取做一致,到时候看jstack方便点)顺序获得lock1和lock2,并在最里层马上调用lock1.wait()。为了保证线程task2在task1释放锁之后尝试获取锁,task2在一开始先sleep 2秒。注意线程task2尝试synchronized的锁一定要和task1调用wait()关联的锁不一样,否则task2马上能够获得锁。这里task2尝试获取lock2,如果线程调用wait()只释放关联的锁对象,那么task2获取不到lock2,会阻塞在那;否则task2获取lock2成功,马上打印出字符串。
运行上面的程序,从控制台(图1)可以看出线程task2并没有进入synchronized块。然后我们通过jstack工具看下这两个线程的具体状态。
图1
首先在命令行输入“jps -l”,获得图2所示:
图2
可以找到我们运行程序对应的进程号pid为27584。jps这个jdk工具可以查看当前用户java进程的简要状态,参数有l和v,具体用法可以网上查找。注意jps只能给出归属当前用户的java进程,要是想查找全部的java进程,windows下要利用下任务管理器,linux下需要top或者ps命令。
然后我们在命令行输入“jstack 27584”,获得图3所示:
图3
jstack具体展示了某个jvm进程在某时刻的堆栈快照。我们可以看到线程task1依次获取了两个锁,分别是0x00000000d5a5b500(lock1)和0x00000000d5a5b510(lock2)。随后线程task1进入了waiting(on object monitor)状态,等待的锁对象是0x00000000d5a5b500(lock1),对应代码里的lock1.wait()。
然后看线程task2,处于blocked(on object monitor)状态,等待的锁对象是0x00000000d5a5b510(lock2)。可以证明线程task1在wait后并没有释放掉所有的锁,只是释放了代码里调用wait()的锁。
其实这篇文章的主题是个很琐碎的细节,实际中遇到的情况很少,而且直观上也能想到答案。只是现在一些教材和网络文章中,很多对比sleep和wait,只是说“sleep不释放线程持有的锁,wait释放线程持有的所锁”,wait释放锁、wait释放锁、wait释放锁...(面试要应付的知识点重复三遍?)。很多初学者仅仅记住这个答案,会导致一些误解,其实准确的来说是obj.wait()函数只释放线程持有的obj锁,而不是释放线程所有的锁,或者说wait()函数之所以是成员函数而不是静态函数,就是只和具体的实体关联(大家可以换位思考下为什么Thread.sleep()函数是静态的)。
- (实验)Java一个线程用synchronized嵌套锁多个对象时调用wait()只释放wait函数关联的所对象还是释放所有锁对象
- wait()方法在等待时释放对象锁
- Java 线程 Thread 对象锁的wait和notify解析
- java多线程设计wait/notify机制 (synchronized与对象锁)
- java多线程设计wait/notify机制 (synchronized与对象锁)
- java wait()notify释放锁
- 理解线程的等待释放(wait/notify)
- wait ,notfiy 配合synchronized关键字使用,wait立即释放锁,而notify并不立即释放
- 初探java 对象中wait(),notify(),notifyAll() 和线程中的synchronized
- multithreading--synchronized对象锁以及对象的notify(),notifyAll(),wait()等的用法
- 线程内核对象的释放问题
- GDI+对象的释放
- autorelease对象的释放
- .NET对象的释放
- 线程问题3(synchronized,wait,notify,notifyAll,类锁,对象锁)
- Java保证程序结束时调用释放资源函数
- java对象方法wait,notify
- Java线程同步:synchronized锁住的是代码还是对象
- 文件上传(多文件)
- 洛谷 P1198 [JSOI2008]最大数
- 设计模式--创建型模式简介及总结
- Spring 使用注解的方式实现IOC和DI(控制反转和依赖注入)
- android studio多项目引用同一Module
- (实验)Java一个线程用synchronized嵌套锁多个对象时调用wait()只释放wait函数关联的所对象还是释放所有锁对象
- Python相关基础
- 计算某年某月某日是该年的第几天【算法入门】
- 栈的存储结构和应用
- layui
- MIPS汇编练习
- MSSQL附加数据库5120错误(拒绝访问)处理方法
- EditPlus实现自动提示和跳转,下载配置CTags,附上下载链接
- Android studio配置copyright插入版权信息的详细教程