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 ...
随机推荐
- 【BZOJ3124】[Sdoi2013]直径 树形DP(不用结论)
[BZOJ3124][Sdoi2013]直径 Description 小Q最近学习了一些图论知识.根据课本,有如下定义.树:无回路且连通的无向图,每条边都有正整数的权值来表示其长度.如果一棵树有N个节 ...
- 【BZOJ2150】部落战争 最小流
[BZOJ2150]部落战争 Description lanzerb的部落在A国的上部,他们不满天寒地冻的环境,于是准备向A国的下部征战来获得更大的领土. A国是一个M*N的矩阵,其中某些地方是城镇, ...
- EasyNVR内网摄像机接入网关+EasyNVS云端管理平台,组件起一套轻量级类似于企业级萤石云的解决方案
背景分析 对于EasyNVR我们应该都了解,主要应用于互联安防直播,对于EasyNVR,我们可以清楚的发现,EasyNVR的工作机制是EasyNVR拉取摄像机的RTSP/Onvif视频流,然后客户端可 ...
- 记录-java(jxl) Excel导入数据库
本内容主要包括(文件上传.excel2003数据导入数据库)excel导入数据库功能需要jxl jar包支持 下面是文件上传的前端测试代码 <%@ page language="ja ...
- IntelliJ IDEA For Mac 快捷键 [转]
Mac键盘符号和修饰键说明 ⌘ Command ⇧ Shift ⌥ Option ⌃ Control ↩︎ Return/Enter ⌫ Delete ⌦ 向前删除键(Fn+Delete) ↑ 上箭头 ...
- ECMAScript6重复字符串方法repeat()
repeat() 1.定义 repeat()方法返回一个新字符串,新字符串将对象字符串重复指定次数. 2.语法 str.repeat(count) count:表示新构造的字符串把原字符串重复的次数, ...
- Qt 如何获取一个文件的 Icon 图标?
#include <QPixmap> #include <QString> #include <QFileInfo> #include <QFileIconP ...
- 谷歌浏览器input中的text 和 button 水平对齐的问题
方法一 text 的vertical-align :top; 方法二 button的vertical-align: middle;
- 0x08 MySQL 超详细-索引原理&慢查询优化【转-多图】(重点)
Content From——Egon's Blog http://www.cnblogs.com/linhaifeng/articles/7274563.html#top 0x01 介绍 为何要有索引 ...
- 【转】api网关
微服务之API网关 一.引言 随着互联网的快速发展,当前以步入移动互联.物联网时代.用户访问系统入口也变得多种方式,由原来单一的PC客户端,变化到PC客户端.各种浏览器.手机移动端及智能终端等.同时系 ...