Java内部锁的可重用性(Reentrancy)
Java提供了强制原子性的内部锁机制:synchronized块。但是内部锁是可重入的,当线程试图获得它自己占有的锁时,请求会成功。
简单的说,就是在一个synchronized方法内部调用本类的其他synchronized方法时,永远可以拿到锁。
如例子1.1所示
- public class LoggingWidget extends Widget{
- public static void main(String[] args) {
- LoggingWidget lw=new LoggingWidget();
- lw.doSomething();
- }
- public synchronized void doSomething(){
- System.out.println("LoggingWidget->doSomething()");
- doAnotherThing(); //调用自己类中其他的synchronized方法
- super.doSomething(); //调用父类的synchronized方法
- }
- private synchronized void doAnotherThing(){
- System.out.println("LoggingWidget->doAnotherThing()");
- }
- }
- class Widget{
- public synchronized void doSomething(){
- System.out.println("Widget->doSomething()");
- }
- }
执行结果是:
- LoggingWidget->doSomething()
- LoggingWidget->doAnotherThing()
- Widget->doSomething()
可见,在java内部,调用父类的synchronized方法和调用自己类中其他synchronized方法都不会阻碍该程序的运行,正是因为java线程是基于“每线程(per-thread)”,而不是基于“每调用的(per-invocation)”的。重进入的实现是通过为每个锁关联一个请求计数和一个占有它的线程。
更有甚者会出现无限递归的情况:
- public class reentry{
- int i=0;
- public static void main(String[] args) {
- reentry lw=new reentry();
- lw.doSomething();
- }
- public synchronized void doSomething(){
- System.out.println("doSomething()"+i++);
- doSomething(); //synchronized方法递归调用
- }
- }
输出结果:
.
- .....
- doSomething()3831
- doSomething()3832
- doSomething()3833
- Exception in thread "main" java.lang.StackOverflowError
- ......
可以看到,程序进入了死循环,直到堆栈溢出。但是不会将锁释放出去。
=============================================================
要记住一句话“重进入的实现是通过为每个锁关联一个请求计数和一个占有它的线程”,就是说如果有其他线程调用该对象的某个方法,那么该对象的其他synchronized方法并不能与其重进入,而是互斥,因为占有该函数的进程不一样,看如下例子:
- public class LoggingWidget {
- static public int i=0;
- public int ii=0;
- public LoggingWidget() {
- super();
- }
- public static void main(String[] args) {
- int totalNumOfThread=20; //有20个线程同时执行
- LoggingWidget lw=new LoggingWidget(); //每个线程都关联同一个LoggingWidget对象
- ArrayList<outer> o=new ArrayList<outer>();
- for(int s=0;s<totalNumOfThread;s++) //为20个线程赋值同一个LoggingWidget对象的引用
- {
- outer t=new outer();
- t.lw=lw;
- o.add(t);
- }
- for(int s=0;s<totalNumOfThread;s++)
- {
- new Thread((outer)o.get(s)).start(); //启动20个线程
- }
- }
- public void doSomething(){ //注意,这里没有给方法synchronized属性
- int sleep=(int)(Math.random()*500); //随机产生一个睡眠时间
- try {
- Thread.sleep(sleep); //睡眠
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- ii=i++; //为每个线程赋予一个ID,ID自增
- try {
- Thread.sleep(sleep); //继续睡眠
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(ii+"线程执行LoggingWidget->doSomething(),睡眠时间:"+sleep);
- }
- }
- class outer extends Thread
- {
- public LoggingWidget lw;
- public outer() {
- super();
- }
- @Override
- public void run() {
- lw.doSomething();
- super.run();
- }
- }
由于没有给方法synchronized属性,所以输出结果如下:
1线程执行LoggingWidget->doSomething(),睡眠时间:4
1线程执行LoggingWidget->doSomething(),睡眠时间:4
3线程执行LoggingWidget->doSomething(),睡眠时间:56
8线程执行LoggingWidget->doSomething(),睡眠时间:108
11线程执行LoggingWidget->doSomething(),睡眠时间:151
11线程执行LoggingWidget->doSomething(),睡眠时间:157
12线程执行LoggingWidget->doSomething(),睡眠时间:177
13线程执行LoggingWidget->doSomething(),睡眠时间:192
14线程执行LoggingWidget->doSomething(),睡眠时间:213
16线程执行LoggingWidget->doSomething(),睡眠时间:218
17线程执行LoggingWidget->doSomething(),睡眠时间:232
19线程执行LoggingWidget->doSomething(),睡眠时间:280
19线程执行LoggingWidget->doSomething(),睡眠时间:354
19线程执行LoggingWidget->doSomething(),睡眠时间:358
19线程执行LoggingWidget->doSomething(),睡眠时间:401
19线程执行LoggingWidget->doSomething(),睡眠时间:428
19线程执行LoggingWidget->doSomething(),睡眠时间:437
19线程执行LoggingWidget->doSomething(),睡眠时间:455
19线程执行LoggingWidget->doSomething(),睡眠时间:468
19线程执行LoggingWidget->doSomething(),睡眠时间:498
如果方法public void doSomething()改成public synchronized void doSomething(),那么输出结果为:
0线程执行LoggingWidget->doSomething(),睡眠时间:384
1线程执行LoggingWidget->doSomething(),睡眠时间:26
2线程执行LoggingWidget->doSomething(),睡眠时间:391
3线程执行LoggingWidget->doSomething(),睡眠时间:289
4线程执行LoggingWidget->doSomething(),睡眠时间:266
5线程执行LoggingWidget->doSomething(),睡眠时间:248
6线程执行LoggingWidget->doSomething(),睡眠时间:121
7线程执行LoggingWidget->doSomething(),睡眠时间:395
8线程执行LoggingWidget->doSomething(),睡眠时间:454
9线程执行LoggingWidget->doSomething(),睡眠时间:457
10线程执行LoggingWidget->doSomething(),睡眠时间:181
11线程执行LoggingWidget->doSomething(),睡眠时间:170
12线程执行LoggingWidget->doSomething(),睡眠时间:470
13线程执行LoggingWidget->doSomething(),睡眠时间:444
14线程执行LoggingWidget->doSomething(),睡眠时间:114
15线程执行LoggingWidget->doSomething(),睡眠时间:4
16线程执行LoggingWidget->doSomething(),睡眠时间:40
17线程执行LoggingWidget->doSomething(),睡眠时间:320
18线程执行LoggingWidget->doSomething(),睡眠时间:416
19线程执行LoggingWidget->doSomething(),睡眠时间:148
可见,不同线程调用同一个对象的synchronized方法,是不会重进入的,而是产生互斥锁.
转自:http://blog.csdn.net/haydenwang8287/article/details/5837734
Java内部锁的可重用性(Reentrancy)的更多相关文章
- Java类锁和对象锁实践和内部私有锁关联
Java类锁和对象锁实践 感谢[jiehao]同学的投稿,投稿可将文章发送到tengfei@ifeve.com 类锁和对象锁是否会冲突?对象锁和私有锁是否会冲突?通过实例来进行说明. 一.相关约定 为 ...
- Java使用泛型类来提高方法的可重用性
我的技术博客经常被流氓网站恶意爬取转载.请移步原文:http://www.cnblogs.com/hamhog/p/3832268.html,享受整齐的排版.有效的链接.正确的代码缩进.更好的阅读体验 ...
- java的锁机制
一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在Java里边就是拿到某个同步对象的锁(一个对象只有一把锁): 如果这个时候同步对象的锁被其他线程拿走了,他(这个线 ...
- Class撑起了OOP世界的天。Class类是OO的基本单元,OO的世界都是通过一个一个的类协作完成的,提高软件的重用性、灵活性和扩展性(转)
引言 在OO的工作中,我们一定会涉及到类,抽象类和接口.那么类和抽象类以及接口到底扮演的什么角色? 本文主要是从人类社会的角度阐述类与抽象类以及接口的“社会”关系,从而让我们抛弃书上的那些死记硬背的概 ...
- java的锁机制——synchronized
一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在java里边就是拿到某个同步对象的锁(一个对象只有一把锁): 如果这个时候同步对象的锁被其他线程拿走了,他(这个线 ...
- Java - “JUC”锁
[Java并发编程实战]-----“J.U.C”:锁,lock 在java中有两种方法实现锁机制,一种是在前一篇博客中([java7并发编程实战]-----线程同步机制:synchronized) ...
- Java 各种锁的小结
一. synchronized 在 JDK 1.6 之前,synchronized 是重量级锁,效率低下. 从 JDK 1.6 开始,synchronized 做了很多优化,如偏向锁.轻量级锁.自旋锁 ...
- Java悲观锁和乐观锁
悲观的并发策略——Synchronized互斥锁 互斥锁是最常见的同步手段,在并发过程中,当多条线程对同一个共享数据竞争时,它保证共享数据同一时刻只能被一条线程使用,其他线程只有等到锁释放后才能重新进 ...
- 深入理解JVM(③)Java的锁优化
前言 从JDK5到JDK6HotSpot虚拟机开发团队花费了大量的资源实现了各种锁优化技术,如适应性自旋(Adaptive Spinning).锁消除(Lock Elimination).锁膨胀(Lo ...
随机推荐
- Introduction to Mathematical Thinking - Week 7
Q: Why did nineteenth century mathematicians devote time to the proof of self-evident results? Selec ...
- 疯狂创客圈 JAVA死磕系列 总目录
无编程不创客,无案例不学习.疯狂创客圈,一大波高手正在交流.学习中! 疯狂创客圈 Java 死磕系列: [博客园 总入口] QQ群:104131248 [Java 聊天室] 实战从0开始,打造100 ...
- DOM 常见事件
onclick //当用户点击某个对象时调用的事件句柄. ondblclick //当用户双击某个对象时调用的事件句柄. onfocus //元素获得焦点. onblur //元素失去焦点. 应用场景 ...
- Linux vim编写程序时出现高亮字符,如何取消?
在“命令模式”下输入“:nohl“,再按回车,便可以取消高亮显示.
- docker 批量导出脚本
用for 循环实现 docker imagesREPOSITORY TAG ...
- android 支付宝集成 使用常见错误
版权声明:好记星不如烂笔头,欢迎批判转载 https://blog.csdn.net/ab601026460/article/details/34956365 1:自己近期在做了支付.遇到了一下问题先 ...
- java定时器的使用(Timer)(转发:https://blog.csdn.net/ecjtuxuan/article/details/2093757)
1.在应用开发中,经常需要一些周期性的操作,比如每5分钟执行某一操作等. 对于这样的操作最方便.高效的实现方式就是使用java.util.Timer工具类. private java.util.Tim ...
- cut命令学习
cut最基本的用法: -f 列号:提取第几列 -d 分隔符:按照指定分隔符分割列(默认是制表符tab) 测试用例:(制表符)
- php……流程
流程:由两个及以上的业务步骤,完成一个完整的业务行为的过程,可称之为流程:注意是两个及以上的业务步骤.事物进行过程中的次序或顺序的布置和安排. 创建页面: 登录页面(login.php): <h ...
- $GitHub边用边总结
以下用法是在ubuntu系统下的用法,主要内容整理自'廖雪峰的官方网站'. #1.安装git$ sudo apt-get install git #2.声明git账号$ git config --gl ...