Java内部锁的可重用性(Reentrancy)

Java提供了强制原子性的内部锁机制:synchronized块。但是内部锁是可重入的,当线程试图获得它自己占有的锁时,请求会成功。

简单的说,就是在一个synchronized方法内部调用本类的其他synchronized方法时,永远可以拿到锁。

如例子1.1所示

[java] view plaincopy

  1. public class LoggingWidget extends Widget{
  2. public static void main(String[] args) {
  3. LoggingWidget lw=new LoggingWidget();
  4. lw.doSomething();
  5. }
  6. public synchronized void doSomething(){
  7. System.out.println("LoggingWidget->doSomething()");
  8. doAnotherThing();         //调用自己类中其他的synchronized方法
  9. super.doSomething();   //调用父类的synchronized方法
  10. }
  11. private synchronized void doAnotherThing(){
  12. System.out.println("LoggingWidget->doAnotherThing()");
  13. }
  14. }
  15. class Widget{
  16. public synchronized void doSomething(){
  17. System.out.println("Widget->doSomething()");
  18. }
  19. }

执行结果是:

[java] view plaincopy

  1. LoggingWidget->doSomething()
  2. LoggingWidget->doAnotherThing()
  3. Widget->doSomething()

可见,在java内部,调用父类的synchronized方法和调用自己类中其他synchronized方法都不会阻碍该程序的运行,正是因为java线程是基于“每线程(per-thread)”,而不是基于“每调用的(per-invocation)”的。重进入的实现是通过为每个锁关联一个请求计数和一个占有它的线程

更有甚者会出现无限递归的情况:

[java] view plaincopy

  1. public class reentry{
  2. int i=0;
  3. public static void main(String[] args) {
  4. reentry lw=new reentry();
  5. lw.doSomething();
  6. }
  7. public synchronized void doSomething(){
  8. System.out.println("doSomething()"+i++);
  9. doSomething();    //synchronized方法递归调用
  10. }
  11. }

输出结果:
.

[java] view plaincopy

  1. .....
  2. doSomething()3831
  3. doSomething()3832
  4. doSomething()3833
  5. Exception in thread "main" java.lang.StackOverflowError
  6. ......

可以看到,程序进入了死循环,直到堆栈溢出。但是不会将锁释放出去。
=============================================================
要记住一句话“重进入的实现是通过为每个锁关联一个请求计数和一个占有它的线程”,就是说如果有其他线程调用该对象的某个方法,那么该对象的其他synchronized方法并不能与其重进入,而是互斥,因为占有该函数的进程不一样,看如下例子:

[java] view plaincopy

  1. public class LoggingWidget {
  2. static public int i=0;
  3. public int ii=0;
  4. public LoggingWidget() {
  5. super();
  6. }
  7. public static void main(String[] args) {
  8. int totalNumOfThread=20;                          //有20个线程同时执行
  9. LoggingWidget lw=new LoggingWidget();  //每个线程都关联同一个LoggingWidget对象
  10. ArrayList<outer> o=new ArrayList<outer>();
  11. for(int s=0;s<totalNumOfThread;s++)        //为20个线程赋值同一个LoggingWidget对象的引用
  12. {
  13. outer t=new outer();
  14. t.lw=lw;
  15. o.add(t);
  16. }
  17. for(int s=0;s<totalNumOfThread;s++)
  18. {
  19. new Thread((outer)o.get(s)).start();  //启动20个线程
  20. }
  21. }
  22. public void doSomething(){                         //注意,这里没有给方法synchronized属性
  23. int sleep=(int)(Math.random()*500);     //随机产生一个睡眠时间
  24. try {
  25. Thread.sleep(sleep);                      //睡眠
  26. } catch (InterruptedException e) {
  27. e.printStackTrace();
  28. }
  29. ii=i++;                                                   //为每个线程赋予一个ID,ID自增
  30. try {
  31. Thread.sleep(sleep);                     //继续睡眠
  32. } catch (InterruptedException e) {
  33. e.printStackTrace();
  34. }
  35. System.out.println(ii+"线程执行LoggingWidget->doSomething(),睡眠时间:"+sleep);
  36. }
  37. }
  38. class outer extends Thread
  39. {
  40. public LoggingWidget lw;
  41. public outer() {
  42. super();
  43. }
  44. @Override
  45. public void run() {
  46. lw.doSomething();
  47. super.run();
  48. }
  49. }

由于没有给方法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方法,是不会重进入的,而是产生互斥锁.

时间: 2024-08-28 11:03:56

Java内部锁的可重用性(Reentrancy)的相关文章

Java类锁和对象锁实践和内部私有锁关联

Java类锁和对象锁实践 感谢[jiehao]同学的投稿,投稿可将文章发送到[email protected] 类锁和对象锁是否会冲突?对象锁和私有锁是否会冲突?通过实例来进行说明. 一.相关约定 为了明确后文的描述,先对本文涉及到的锁的相关定义作如下约定: 1. 类锁:在代码中的方法上加了static和synchronized的锁,或者synchronized(xxx.class)的代码段,如下文中的increament(): 2.对象锁:在代码中的方法上加了synchronized的锁,或者

Java - “JUC”锁

[Java并发编程实战]-----"J.U.C":锁,lock 在java中有两种方法实现锁机制,一种是在前一篇博客中([java7并发编程实战]-----线程同步机制:synchronized)介绍的synchronized,而另一种是比synchronized更加强大和领过的Lock.Lock确保当一个线程位于代码的临界区时,另一个线程不进入临界区,相对于synchronized,Lock接口及其实现类提供了更加强大.灵活的锁机制. 一个简单的锁 在使用synchronized时,

java的锁机制

一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在java里边就是拿到某个同步对象的锁(一个对象只有一把锁): 如果这个时候同步对象的锁被其他线程拿走了,他(这个线程)就只能等了(线程阻塞在锁池等待队列中). 取到锁后,他就开始执行同步代码(被synchronized修饰的代码):线程执行完同步代码后马上就把锁还给同步对象,其他在锁池中等待的某个线程就可以拿到锁执行同步代码了.这样就保证了同步代码在统一时刻只有一个线程在执行. 众所周知,在Java多线程编程中

java的锁机制——synchronized

转自:http://blog.csdn.net/yangzhijun_cau/article/details/6432216 一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在java里边就是拿到某个同步对象的锁(一个对象只有一把锁): 如果这个时候同步对象的锁被其他线程拿走了,他(这个线程)就只能等了(线程阻塞在锁池等待队列中). 取到锁后,他就开始执行同步代码(被synchronized修饰的代码):线程执行完同步代码后马上就把锁还给同步对象,其他在锁池中

java中锁机制

一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在java里边就是拿到某个同步对象的锁(一个对象只有一把锁): 如果这个时候同步对象的锁被其他线程拿走了,他(这个线程)就只能等了(线程阻塞在锁池等待队列中). 取到锁后,他就开始执行同步代码(被synchronized修饰的代码):线程执行完同步代码后马上就把锁还给同步对象,其他在锁池中等待的某个线程就可以拿到锁执行同步代码了.这样就保证了同步代码在统一时刻只有一个线程在执行.   众所周知,在Java多线程编

java对象锁和类锁

参考 http://www.cnblogs.com/yyyyy5101/archive/2011/07/20/2112157.html http://www.cnblogs.com/kkcheng/archive/2011/02/25/1964521.html http://my.oschina.net/billowworld/blog/120766 1.java对象锁 所有对象都自动含有单一的锁.JVM负责跟踪对象被加锁的次数.如果一个对象被解锁,其计数变为0.在任务(线程)第一次给对象加锁的

Class撑起了OOP世界的天。Class类是OO的基本单元,OO的世界都是通过一个一个的类协作完成的,提高软件的重用性、灵活性和扩展性(转)

引言 在OO的工作中,我们一定会涉及到类,抽象类和接口.那么类和抽象类以及接口到底扮演的什么角色? 本文主要是从人类社会的角度阐述类与抽象类以及接口的“社会”关系,从而让我们抛弃书上的那些死记硬背的概念,快速理解他们的区别与联系? 如果大家觉得还有必要把这块更新的更好,还请多多反馈. 如果觉的对您有用还请点击 “推荐”下,我会持续更新更多新的内容. 古老的传说 相传盘古开天劈地后,女娲一天在黄河边梳头时,突发奇想以泥土仿照自己抟土造人,创造并构建人类社会.后来又替人类立下了婚姻制度,经过一代又一

java 分布式锁 -图解- 秒懂

目录 写在前面 1.1. 分布式锁 简介 1.1.1. 图解:公平锁和可重入锁 模型 1.1.2. 图解: zookeeper分布式锁的原理 1.1.3. 分布式锁的基本流程 1.1.4. 加锁的实现 1.1.5. 释放锁的实现 1.1.1. 分布式锁的应用场景 写在最后 疯狂创客圈 亿级流量 高并发IM 实战 系列 疯狂创客圈 Java 分布式聊天室[ 亿级流量]实战系列之 -26[ 博客园 总入口 ] 写在前面 ? 大家好,我是作者尼恩.目前和几个小伙伴一起,组织了一个高并发的实战社群[疯狂

内部锁、显示锁和读写锁

线程同步机制 线程同步机制是一套用于协调线程间的数据访问及活动的机制.该机制用于保障线程安全及实现这些线程的共同目标. java平台提供的线程同步机制: 锁 volatile关键字 final关键字 static关键字 其他(如:Object.wait()/Object.notify()等) 锁机制 锁机制 :将多线程并发访问共享数据转换为串行访问,一个共享数据每次只能被一个线程访问(获得锁),该线程访问结束后(释放锁)其他线程才能对其访问. 锁的获得 : 一个线程在访问数据前必须申请相应的锁.