zoukankan      html  css  js  c++  java
  • 多线程学习-基础( 十)一个synchronized(){/*代码块*/}简单案例分析

    一、提出疑惑

       上一篇文章中,分析了synchronized关键字的用法。但是好像遗漏了一种情况。

    那就是:

    synchronized(obj){/*同步块代码*/} 一般有以下几种情况:

    (1)synchronized(this){/*同步块代码*/}:

      synchronized锁住的是this,而this是当前对象的,即当前代码所在类的正在被调用的实例化对象。这个用法在上一篇中已经做了说明,此处就不在阐述了。

    (2)synchronized(otherInstence){/*同步块代码*/}:

      简单说明一下:

        otherInstence是其他类的实例化对象,这种情况被遗漏了。

      所以有疑惑:

       先假设一种情景,类A的实例化对象 a1 ,a2,a3, 类B的实例化对象b1,   A类中某一个方法中存在synchronized(b1){/**/},那么,当多个线程T1,T2,T3分别操作a1,a2,a3这个三个A类的对象,来调用B类的对象b1的时候,

    T1,T2,T3三个线程的执行情况如何呢?线程在a1,a2,a3对象中的权限范围如何呢?

      接下来,通过几个案例来观察分析一下。

    二、简单的案例

      根据上面的假设:

      “先假设一种情景,类A的实例化对象 a1 ,a2,a3, 类B的实例化对象b1,   A类中某一个方法中存在synchronized(b1){/**/},那么,当多个线程T1,T2,T3分别操作a1,a2,a3这个三个A类的对象,来调用B类的对象b1的时候,

    T1,T2,T3三个线程的执行情况如何呢?线程在a1,a2,a3对象中的权限范围如何呢

     我改造了一下,引入了故事情景:

        有一个叫做库林的小和尚(Monk),他所居住的寺院中有一群专门管理整个寺院里小和尚的人(MonasteryManager),他们负者给寺院里的小和尚指派“打水”、“劈柴”,“做饭”,“扎马步”的任务,也有些喜欢给小和尚“讲故事”的等等,这群人中有三个喜欢指使小和尚库林(Monk)的人,分别是“善良A”,“凶恶B”和“武术C”。

      根据上面的情景,我定义了2各类:Monk和MonasteryManager,代码贴出如下:

      小和尚:Monk类

     1 package com.jason.synch;
     2 /**
     3  * 多线程学习:synchronized
     4  * @function      创建一个小和尚的类
     5  * @author 小风微凉
     6  * @time  2018-4-24 下午12:51:23
     7  */
     8 public class Monk {
     9     //小和尚的名字
    10     private String selfName;
    11     
    12     public Monk(String selfName){
    13         this.selfName=selfName;
    14     }
    15     public String getSelfName() {
    16         return selfName;
    17     }
    18 }

       寺院管理者:MonasteryManager类  

     1 package com.jason.synch;
     2 /**
     3  * 多线程学习:synchronized
     4  * @function  创建一个寺院管理人的类    
     5  * @author 小风微凉
     6  * @time  2018-4-24 下午12:59:48
     7  */
     8 public class MonasteryManager {
     9     //寺院管理人的名称
    10     private String managerName;
    11     
    12     public MonasteryManager(String managerName){
    13         this.managerName=managerName;
    14     }
    15     /**
    16      * 小和尚去打水
    17      */
    18     public void thrash(Monk monk,int i){
    19         System.out.println("*************"+this.managerName+"来了,准备布置任务****************");
    20         //锁住小和尚:只有当前的管理者可以让这个小和尚去打水
    21         synchronized(monk){
    22             try {
    23                 System.out.println("任务开始:(第"+i+"次)"+this.managerName+"说:"让小和尚("+monk.getSelfName()+")快去打水",其他寺院管理者等待中。");
    24                 Thread.sleep(10000);
    25             } catch (InterruptedException e) {
    26                 e.printStackTrace();
    27             }
    28             //小和尚打完水后,其他管理者可以命令这个和尚做其他的事情了
    29             System.out.println("小和尚("+monk.getSelfName()+")完成了"+this.managerName+"命令的打水工作,小和尚闲着");
    30             System.out.println("*************"+this.managerName+"离开,任务结束****************");
    31         }        
    32     }
    33     /**
    34      * 小和尚去做饭
    35      */
    36     public void cook(Monk monk,int i){
    37         System.out.println("(第"+i+"次)"+this.managerName+"说:"让小和尚("+monk.getSelfName()+")快去做饭"");
    38     }
    39     /**
    40      * 小和尚去劈柴
    41      */
    42     public void firewood(Monk monk,int i){
    43         System.out.println("(第"+i+"次)"+this.managerName+"说:"让小和尚("+monk.getSelfName()+")快去劈柴"");
    44     }
    45     /**
    46      * 小和尚去扎马步(练功夫)
    47      */
    48     public void kfu(Monk monk,int i){
    49         System.out.println("(第"+i+"次)"+this.managerName+"说:"让小和尚("+monk.getSelfName()+")快去扎马步"");
    50     }
    51     /**
    52      * 小和尚来听故事
    53      */
    54     public void listenStory(Monk monk,int i){
    55         System.out.println("(第"+i+"次)"+this.managerName+"说:"让小和尚("+monk.getSelfName()+")快来听故事"");
    56     }
    57 }

      (1)第一种情况:当“善良A”,“凶恶B”和“武术C”都指使小和尚库林(小和尚库林被同步锁住)去打水。会发生怎么样的情况呢???让我们带着代码去分析一下:

      执行任务的代码:  

     1 package com.jason.synch;
     2 /**
     3  * 多线程学习:synchronized
     4  * @function  开始测试
     5  * @author 小风微凉
     6  * @time  2018-4-24 下午1:10:14
     7  */
     8 public class ThreadStart{
     9     /**
    10      * 启动项
    11      * @throws InterruptedException 
    12      */
    13     public static void main(String[] args) throws InterruptedException {
    14         
    15         //叫来一个小和尚
    16         final Monk littleMonk=new Monk("库林");
    17         //叫来三个寺院管理人
    18         final MonasteryManager manager_1=new MonasteryManager("善良A");//听故事
    19         final MonasteryManager manager_2=new MonasteryManager("凶恶B");//做饭、打水、劈柴
    20         final MonasteryManager manager_3=new MonasteryManager("武术C");//练功夫
    21         //创建三个任务,让管理者指使小和尚去做
    22         new Thread(new Runnable(){
    23             public void run() {
    24                 for(int i=1;i<=3;i++){
    25                     manager_1.thrash(littleMonk,i);//小和尚去打水        
    26                 }    
    27             }            
    28         },"善良线程").start();
    29         new Thread(new Runnable(){
    30             public void run() {
    31                 for(int i=1;i<=3;i++){
    32                     manager_2.thrash(littleMonk,i);//小和尚去打水
    33                 }
    34             }            
    35         },"凶恶线程").start();
    36         new Thread(new Runnable(){
    37             public void run() {
    38                 for(int i=1;i<=3;i++){
    39                     manager_3.thrash(littleMonk,i);//小和尚去打水
    40                 }
    41             }            
    42         },"武术线程").start();
    43     }
    44 }

     首先来看一下,上面的代码的运行结果,然后再逐步分析原因:

    *************善良A来了,准备布置任务****************
    *************凶恶B来了,准备布置任务****************
    任务开始:(第1次)凶恶B说:"让小和尚(库林)快去打水",其他寺院管理者等待中。
    *************武术C来了,准备布置任务****************
    小和尚(库林)完成了凶恶B命令的打水工作,小和尚闲着
    *************凶恶B离开,任务结束****************
    *************凶恶B来了,准备布置任务****************
    任务开始:(第1次)武术C说:"让小和尚(库林)快去打水",其他寺院管理者等待中。
    小和尚(库林)完成了武术C命令的打水工作,小和尚闲着
    *************武术C离开,任务结束****************
    *************武术C来了,准备布置任务****************
    任务开始:(第1次)善良A说:"让小和尚(库林)快去打水",其他寺院管理者等待中。
    小和尚(库林)完成了善良A命令的打水工作,小和尚闲着
    *************善良A离开,任务结束****************
    *************善良A来了,准备布置任务****************
    任务开始:(第2次)武术C说:"让小和尚(库林)快去打水",其他寺院管理者等待中。
    小和尚(库林)完成了武术C命令的打水工作,小和尚闲着
    *************武术C离开,任务结束****************
    任务开始:(第2次)凶恶B说:"让小和尚(库林)快去打水",其他寺院管理者等待中。
    *************武术C来了,准备布置任务****************
    小和尚(库林)完成了凶恶B命令的打水工作,小和尚闲着
    *************凶恶B离开,任务结束****************
    *************凶恶B来了,准备布置任务****************
    任务开始:(第3次)武术C说:"让小和尚(库林)快去打水",其他寺院管理者等待中。
    小和尚(库林)完成了武术C命令的打水工作,小和尚闲着
    *************武术C离开,任务结束****************
    任务开始:(第2次)善良A说:"让小和尚(库林)快去打水",其他寺院管理者等待中。
    小和尚(库林)完成了善良A命令的打水工作,小和尚闲着
    *************善良A离开,任务结束****************
    *************善良A来了,准备布置任务****************
    任务开始:(第3次)凶恶B说:"让小和尚(库林)快去打水",其他寺院管理者等待中。
    小和尚(库林)完成了凶恶B命令的打水工作,小和尚闲着
    *************凶恶B离开,任务结束****************
    任务开始:(第3次)善良A说:"让小和尚(库林)快去打水",其他寺院管理者等待中。
    小和尚(库林)完成了善良A命令的打水工作,小和尚闲着
    *************善良A离开,任务结束****************

     现在来简单分析一下运行结果:

      观察上面的代码:三个线程“善良线程”,“凶恶线程”,“武术线程”分别控制:“善良A”,“凶恶B”,“武术C”这三个寺院管理人,而这三个管理人又一起让小和尚(库林)去打水,打水这个方法中存在:synchronized(小和尚){/**/}

    简单观察一下:打水方法 

     1 /**
     2      * 小和尚去打水
     3      */
     4     public void thrash(Monk monk,int i){
     5         System.out.println("*************"+this.managerName+"来了,准备布置任务****************");
     6         //锁住小和尚:只有当前的管理者可以让这个小和尚去打水
     7         synchronized(monk){
     8             try {
     9                 System.out.println("任务开始:(第"+i+"次)"+this.managerName+"说:"让小和尚("+monk.getSelfName()+")快去打水",其他寺院管理者等待中。");
    10                 Thread.sleep(10000);
    11             } catch (InterruptedException e) {
    12                 e.printStackTrace();
    13             }
    14             //小和尚打完水后,其他管理者可以命令这个和尚做其他的事情了
    15             System.out.println("小和尚("+monk.getSelfName()+")完成了"+this.managerName+"命令的打水工作,小和尚闲着");
    16             System.out.println("*************"+this.managerName+"离开,任务结束****************");
    17         }        
    18     }

     分析知道:由于JVM为每一个对象实例都分配了一把对象锁,而三个线程都分别控制着:“善良A”,“凶恶B”,“武术C”这三个不同的对象实例,所以三个线程之间针对MonasteryManager这个类来说,是互补干扰的,即不存在同步现象。

    所以三个线程都可以自由,互不干扰地进入MonasteryManager类实例的打水方法:thrash()。所以我们可以看到:程序开始运行的时候,三个线程都会无序地执行:System.out.println("*************"+this.managerName+"来了,准备布置任务****************");这句代码,所以就有了运行结果中的下面这个现象:

    *************善良A来了,准备布置任务****************
    *************凶恶B来了,准备布置任务****************
    任务开始:(第1次)凶恶B说:"让小和尚(库林)快去打水",其他寺院管理者等待中。
    *************武术C来了,准备布置任务****************

     可以看到:程序开始运行,三个线程开始争夺cpu的执行权限,第一次抢夺成功的是“善良线程”,刚刚执行到:System.out.println("*************"+this.managerName+"来了,准备布置任务****************");这句代码就迫让出对cpu的控制。

    其次是“凶恶线程”,凶恶线程比善良线程多执行了一段时间,并且进入:synchronized(monk){},拿到了小和尚库林的对象锁:

     1 synchronized(monk){
     2             try {
     3                 System.out.println("任务开始:(第"+i+"次)"+this.managerName+"说:"让小和尚("+monk.getSelfName()+")快去打水",其他寺院管理者等待中。");
     4                 Thread.sleep(10000);
     5             } catch (InterruptedException e) {
     6                 e.printStackTrace();
     7             }
     8             //小和尚打完水后,其他管理者可以命令这个和尚做其他的事情了
     9             System.out.println("小和尚("+monk.getSelfName()+")完成了"+this.managerName+"命令的打水工作,小和尚闲着");
    10             System.out.println("*************"+this.managerName+"离开,任务结束****************");
    11         }    

     上面这段代码,只有拿到小和尚库林的对象锁的线程才可以执行。

    继续看运行结果: 

    *************善良A来了,准备布置任务****************      //"善良线程"抢到cpu执行权
    *************凶恶B来了,准备布置任务****************      //"凶恶线程"抢到cpu执行权
    任务开始:(第1次)凶恶B说:"让小和尚(库林)快去打水",其他寺院管理者等待中。//"凶恶线程"抢到了(库林)小和尚的对象锁
    *************武术C来了,准备布置任务****************      //“武术线程”抢到cpu执行权
    小和尚(库林)完成了凶恶B命令的打水工作,小和尚闲着          //“凶恶线程”继续执行sychronized块中的代码
    *************凶恶B离开,任务结束****************        //“凶恶线程”继续执行sychronized块中的代码
    *************凶恶B来了,准备布置任务****************           //“武术线程”抢到cpu执行权

    任务开始:(第1次)武术C说:"让小和尚(库林)快去打水",其他寺院管理者等待中。//“武术线程”抢到(库林)小和尚的对象锁
     小和尚(库林)完成了武术C命令的打水工作,小和尚闲着           //“武术线程”继续执行synchronized块中的代码
    *************武术C离开,任务结束****************          //“武术线程”继续执行synchronized块中的代码

     具体分析,看上面的“蓝色”说明部分。

    (2)第二种情况:当“善良A”,“凶恶B”和“武术C”分别指使小和尚库林(小和尚库林被同步锁住)去打水,劈柴,扎马步 会发生怎么样的情况呢???让我们带着代码去分析一下:

    (备注说明:打水,劈柴,扎马步三个方法中:打水,劈柴这2个方法中有:synchronized(小和尚){/**/}

    修改部分:MonasteryManager类的方法

     1 /**
     2      * 小和尚去打水
     3      */
     4     public void thrash(Monk monk,int i){
     5         System.out.println("*************"+this.managerName+"来了,准备布置打水任务****************");
     6         //锁住小和尚:只有当前的管理者可以让这个小和尚去打水
     7         synchronized(monk){
     8             try {
     9                 System.out.println("任务开始:(第"+i+"次)"+this.managerName+"说:"让小和尚("+monk.getSelfName()+")快去打水",其他寺院管理者等待中。");
    10                 Thread.sleep(10000);
    11             } catch (InterruptedException e) {
    12                 e.printStackTrace();
    13             }
    14             //小和尚打完水后,其他管理者可以命令这个和尚做其他的事情了
    15             System.out.println("小和尚("+monk.getSelfName()+")完成了"+this.managerName+"命令的打水工作,小和尚闲着");
    16             System.out.println("*************"+this.managerName+"离开,任务结束****************");
    17         }        
    18     }
    19     /**
    20      * 小和尚去做饭
    21      */
    22     public void cook(Monk monk,int i){
    23         System.out.println("(第"+i+"次)"+this.managerName+"说:"让小和尚("+monk.getSelfName()+")快去做饭"");
    24     }
    25     /**
    26      * 小和尚去劈柴
    27      */
    28     public void firewood(Monk monk,int i){
    29         System.out.println("*************"+this.managerName+"来了,准备布置劈柴任务****************");
    30         //锁住小和尚:只有当前的管理者可以让这个小和尚去劈柴
    31         synchronized(monk){
    32             try {
    33                 System.out.println("任务开始:(第"+i+"次)"+this.managerName+"说:"让小和尚("+monk.getSelfName()+")快去劈柴",其他寺院管理者等待中。");
    34                 Thread.sleep(10000);
    35             } catch (InterruptedException e) {
    36                 e.printStackTrace();
    37             }
    38             //小和尚劈柴后,其他管理者可以命令这个和尚做其他的事情了
    39             System.out.println("小和尚("+monk.getSelfName()+")完成了"+this.managerName+"命令的劈柴工作,小和尚闲着");
    40             System.out.println("*************"+this.managerName+"离开,任务结束****************");
    41         }    
    42     }
    43     /**
    44      * 小和尚去扎马步(练功夫)
    45      */
    46     public void kfu(Monk monk,int i){
    47         System.out.println("(第"+i+"次)"+this.managerName+"说:"让小和尚("+monk.getSelfName()+")快去扎马步"");
    48     }

      接下来是启动任务的代码:

     1 //创建三个任务,让管理者指使小和尚去做
     2         new Thread(new Runnable(){
     3             public void run() {
     4                 for(int i=1;i<=3;i++){
     5                     manager_1.thrash(littleMonk,i);//小和尚去打水        
     6                 }    
     7             }            
     8         },"善良线程").start();
     9         new Thread(new Runnable(){
    10             public void run() {
    11                 for(int i=1;i<=3;i++){
    12                     manager_2.thrash(littleMonk,i);//小和尚去劈柴
    13                 }
    14             }            
    15         },"凶恶线程").start();
    16         new Thread(new Runnable(){
    17             public void run() {
    18                 for(int i=1;i<=3;i++){
    19                     manager_3.thrash(littleMonk,i);//小和尚去扎马步
    20                 }
    21             }            
    22         },"武术线程").start();

     查看运行结果:

    *************善良A来了,准备布置打水任务****************
    任务开始:(第1次)善良A说:"让小和尚(库林)快去打水",其他寺院管理者等待中。
    *************凶恶B来了,准备布置劈柴任务****************
    (第1次)武术C说:"让小和尚(库林)快去扎马步"    //"武术线程“连续抢夺cpu执行权成功,率先完成让小和尚完成了任务
    (第2次)武术C说:"让小和尚(库林)快去扎马步"
    (第3次)武术C说:"让小和尚(库林)快去扎马步"
    小和尚(库林)完成了善良A命令的打水工作,小和尚闲着
    *************善良A离开,任务结束****************
    *************善良A来了,准备布置打水任务****************
    任务开始:(第1次)凶恶B说:"让小和尚(库林)快去劈柴",其他寺院管理者等待中。//当凶恶线程拿到小和尚对象锁的控制权的时候,即在线程执行synchronized块中的代码之后当一个线程执行完毕之后,才会释放对象锁,其他线程才有机会拿到对象锁,进入执行synchronized代码块
    小和尚(库林)完成了凶恶B命令的劈柴工作,小和尚闲着
    *************凶恶B离开,任务结束****************
    任务开始:(第2次)善良A说:"让小和尚(库林)快去打水",其他寺院管理者等待中。
    *************凶恶B来了,准备布置劈柴任务****************
    小和尚(库林)完成了善良A命令的打水工作,小和尚闲着
    *************善良A离开,任务结束****************
    任务开始:(第2次)凶恶B说:"让小和尚(库林)快去劈柴",其他寺院管理者等待中。
    *************善良A来了,准备布置打水任务****************
    小和尚(库林)完成了凶恶B命令的劈柴工作,小和尚闲着
    *************凶恶B离开,任务结束****************
    *************凶恶B来了,准备布置劈柴任务****************
    任务开始:(第3次)善良A说:"让小和尚(库林)快去打水",其他寺院管理者等待中。
    小和尚(库林)完成了善良A命令的打水工作,小和尚闲着
    *************善良A离开,任务结束****************
    任务开始:(第3次)凶恶B说:"让小和尚(库林)快去劈柴",其他寺院管理者等待中。
    小和尚(库林)完成了凶恶B命令的劈柴工作,小和尚闲着
    *************凶恶B离开,任务结束****************

     在线程执行synchronized块中的代码之后当一个线程执行完毕之后,才会释放对象锁,其他线程才有机会拿到对象锁,进入执行synchronized代码块,观察上面的运行结果,需要注意的是:拿到对象所权限的线程,这个

    执行权限的范围需特别注意,本案例中的对象锁权限是:小和尚(库林)的使用权。
      打水方法:thrash()     synchronized(monk){} 并操作了:小和尚(库林)    会受到线程的同步互斥影响 拿到当前库林对象锁的线程才可以继续操作synchronized块中的代码
      劈柴方法:firewood()    synchronized(monk){} 并操作了:小和尚(库林)     会受到线程同步的互斥影响     拿到当前库林对象锁的线程才可以继续操作synchronized块中的代码
      扎马步方法:kfu()      没有synchronized(monk){} 没有操作小和尚(库林) 不会受到线程同步的互斥影响 线程都可以执行
    三、归纳总结(针对:synchronized(obj){/**/}这种模式)
    1、只有拿到对象锁的线程才可以执行synchronized块中的代码,当前拿到锁的线程尚未执行执行完毕且释放锁,其他线程如果想要执行synchronized块中的代码,就需要等待(处于阻塞状态)。
    2、synchronized(obj):obj对象锁,锁的范围就是obj对象的使用权限,拿到obj对象锁的线程有权限使用obj对象的功能,没有obj对象锁的线程则没有权利使用obj的功能。
    3、多个线程只要不去操作被锁住的对象,那么这些多个线程执行其他一些非synchronized块的代码是不会相互影响的。
      
  • 相关阅读:
    如何在linux下使用sudo命令不用输入密码
    QT读写ini配置文件
    dubbo的灰度发布
    dubbo的重试原则
    dubbo的启动时检查
    dubbo的超时处理和配置覆盖
    dubbo配置文件加载顺序
    java基础-泛型的优点
    idea提交多个项目到github的同一个repository
    【掌握】dubbo环境搭建linux
  • 原文地址:https://www.cnblogs.com/newwind/p/8929869.html
Copyright © 2011-2022 走看看