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

  • 相关阅读:
    dd是___元素
    【电商8】footer mod_service
    外边距塌陷问题
    隐藏display: ____;
    判断一个32位的整数的二进制有几个1
    8 switch case
    键盘录入
    两个整数的交换
    运算符
    java 异常报错总结
  • 原文地址:https://www.cnblogs.com/bendantuohai/p/4566782.html
Copyright © 2020-2023  润新知