synchronized关键字和内置锁理解
来源:互联网 发布:今晚非农数据最新消息 编辑:程序博客网 时间:2024/06/05 05:54
在学习《JAVA并发编程实战》 2.3.2 重入的时候,有一段介绍不是特别理解,后来查看资料有了一些理解,把自己的理解写在下面,也方便自己以后查看
在介绍重入的时候,书中有这样一段代码和介绍
class Widget { public synchronized void doSomething() { }}class LoggingWidget extends Widget { public synchronized void doSomething() { System.out.println(toString() + ": calling doSomething"); super.doSomething(); }}
由于Widget和LoggingWidget中doSomething方法都是synchronized方法,因此每个doSomthing方法在执行前都会获得Widget上的锁。然而,如果内置锁是不可重入的,那么调用super.doSomething时将无法获得Widget上的锁。
我一开始的理解是:
LoggingWidget和Widget为父子类关系,应该各自有一把锁。如果不是一把锁为什么能重入呢?不是应该先获得LoggingWidget的锁,再去获得Widget的锁?
难道是说LoggingWidget继承父类以后,在LoggingWidget中集成了父类的方法,所以只有LoggingWidget一把锁?
后来经过查阅以后,正确的姿势应该是:
LoggingWidget和Widget上是在方法上加的synchronized关键字,为对象锁,等同于
synchronized(this){XXX}
而关键点在于,锁的持有人并不是LoggingWidget类或者Widget类,而是this这个关键字,即是调用这个方法的对象。(比如 LoggingWidget lw = new LoggingWidget(),那么锁的持有人为lw,这个锁锁住的是对象实例,没有父类子类的区别)
因为调用doSomething()和super.doSomething()为同一个对象。所以需要同一把锁才能进入。
下面通过一段完整的代码来模拟一下
class Widget { public synchronized void doSomething() { System.out.println(Thread.currentThread().getName() + ": calling Widget doSomething"); NonreentrantDeadlock.countDown(2); // 线程休眠3秒 System.out.println(Thread.currentThread().getName() + ": leave Widget doSomething"); }}class LoggingWidget extends Widget implements Runnable{ public synchronized void doSomething() { System.out.println(Thread.currentThread().getName() + ": calling LoggingWidget doSomething and get Lock"); NonreentrantDeadlock.countDown(2); // 线程休眠3秒 super.doSomething(); System.out.println(Thread.currentThread().getName() + ": leave LoggingWidget doSomething"); } @Override public void run() { doSomething(); }}public class NonreentrantDeadlock { public static void main(String[] args) { LoggingWidget lw = new LoggingWidget(); Thread t1 = new Thread(lw,"乌龟"); // 创建叫乌龟的线程,传入lw Thread t2 = new Thread(lw,"兔子"); // 创建叫兔子的线程,传入lw t1.start(); // 启动线程 t2.start(); } public static void countDown(int num) { while (num > 0) { try { Thread.sleep(1000); System.out.println("倒计时:" + num + "s"); num--; } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("倒计时结束"); }}
运行结果
**兔子: calling LoggingWidget doSomething and get Lock
倒计时:2s
倒计时:1s
兔子: calling Widget doSomething
倒计时:2s
倒计时:1s
兔子: leave Widget doSomething
兔子: leave LoggingWidget doSomething
乌龟: calling LoggingWidget doSomething and get Lock
倒计时:2s
倒计时:1s
乌龟: calling Widget doSomething
倒计时:2s
倒计时:1s
乌龟: leave Widget doSomething
乌龟: leave LoggingWidget doSomething**
首先我们创建了两个线程乌龟(t1)和兔子(t2),传入同一个对象(lw),而且synchronized都标记在方法上。所所以乌龟和兔子调用的所有同步方法只有一把锁。锁的持有者为lw。
首先兔子抢到了先机,先进入了LoggingWidget的doSomething()方法,他发现锁在方法上,所以持有者为lw,而且lw的锁还在,并获得了lw的锁。
此时乌龟起步太慢,因为他和兔子都为同一个对象实例他想要进入代码段也需要lw得锁,到了doSomething()方法的时候发现锁被拿走了,只能在门口等着。
之后兔子运行到super.doSomething()的时候,发现这个方法也需要lw的锁,而它正好有这把锁,所以他就直接进入了方法。
运行完成后,兔子离开了LoggingWidget方法,并把锁还了回去。
此时乌龟还在门口等待,看到锁被还回来了,马上获得了锁,然后进入方法内部并执行。
如果我们修改一下代码。
LoggingWidget lw1 = new LoggingWidget(); // 一个LoggingWidget实例 LoggingWidget lw2 = new LoggingWidget(); // 另一个LoggingWidget实例 Thread t1 = new Thread(lw1,"乌龟"); // 创建叫乌龟的线程,传入lw1 Thread t2 = new Thread(lw2,"兔子"); // 创建叫兔子的线程,传入lw2 t1.start(); // 启动线程 t2.start();
那么运行结果为
兔子: calling LoggingWidget doSomething and get Lock
乌龟: calling LoggingWidget doSomething and get Lock
倒计时:2s
倒计时:2s
倒计时:1s
兔子: calling Widget doSomething
倒计时:1s
乌龟: calling Widget doSomething
倒计时:2s
倒计时:2s
倒计时:1s
兔子: leave Widget doSomething
兔子: leave LoggingWidget doSomething
倒计时:1s
乌龟: leave Widget doSomething
乌龟: leave LoggingWidget doSomething
我们发现还是兔子先拿到了锁,但是乌龟同时也拿到了锁。
这是因为,两个线程传入了两个不同的对象lw1和lw2。两个不同对象实例拥有两把不同的锁,即使调用的方法相同,需要的锁也不同。
兔子先进入LoggingWidget的doSomething方法,需要lw1的锁,发现lw1的锁还在,所以它获得了锁,继续执行。
乌龟后来也到了LoggingWidget的doSomething方法,它需要lw2的锁,所以它也能够继续执行。
所以,锁的持有者和方法,类没有关系,重点在于这个锁的持有者是谁。
可以后续思考一下如果这样,锁的持有者是谁呢?
synchronized(LoggingWidget.class) {}
- synchronized关键字和内置锁理解
- java synchronized的理解以及内置锁和对象锁
- 多线程:Java内置锁与synchronized关键字
- Java内置锁与synchronized关键字
- Synchronized关键字中锁的理解
- Synchronized 关键字的理解
- java synchronized关键字理解
- synchronized关键字理解
- synchronized 关键字 加深理解
- synchronized关键字重新理解
- synchronized关键字的理解
- synchronized关键字理解
- synchronized 关键字的理解
- 深入理解synchronized关键字
- 多线程3:Java内置锁与synchronized关键字
- 从Java内存模型理解synchronized、volatile和final关键字
- 对synchronized关键字的理解
- java synchronized内置锁
- 回顾SSM框架的搭建
- Query on a string 线段树
- 生产者/消费者模式(阻塞队列) 一个经典的并发模型
- mybatis核心配置文件mybatis-config.xml
- Altium designer 17安装破解教程(附百度云盘下载)
- synchronized关键字和内置锁理解
- [C#]结构体和类的区别 结构体和类的区别: 在做一个项目时,使用了较多的结构体,并且存在一些结构体的嵌套,即某结构体成员集合包含另一个结构体等,总是出现一些奇怪的错误,才终于下决心好好分析一
- wordpress搭建网站
- try-catch和throw,throws的区别
- 在浏览器中输入 网址 后执行的全部过程
- es6版打灰机游戏 --- 玩家飞机类的构建
- jquery ajax 方法及各参数详解
- 支持语音识别、自然语言理解的微信小程序(“遥知之”智能小秘)完整源码分享
- HTML学习之路01--设置背景