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