1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > 多线程学习-基础( 十)一个synchronized(){/*代码块*/}简单案例分析

多线程学习-基础( 十)一个synchronized(){/*代码块*/}简单案例分析

时间:2019-07-05 22:57:04

相关推荐

多线程学习-基础( 十)一个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 -4-24 下午12:51:23 7 */ 8 public class Monk { 9//小和尚的名字10private String selfName;1112public Monk(String selfName){13 this.selfName=selfName;14}15public String getSelfName() {16 return selfName;17}18 }

寺院管理者:MonasteryManager类

1 package com.jason.synch; 2 /** 3 * 多线程学习:synchronized 4 * @function 创建一个寺院管理人的类5 * @author 小风微凉 6 * @time -4-24 下午12:59:48 7 */ 8 public class MonasteryManager { 9//寺院管理人的名称10private String managerName;1112public MonasteryManager(String managerName){13 this.managerName=managerName;14}15/**16* 小和尚去打水17*/18public 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*/36public void cook(Monk monk,int i){37 System.out.println("(第"+i+"次)"+this.managerName+"说:\"让小和尚("+monk.getSelfName()+")快去做饭\"");38}39/**40* 小和尚去劈柴41*/42public void firewood(Monk monk,int i){43 System.out.println("(第"+i+"次)"+this.managerName+"说:\"让小和尚("+monk.getSelfName()+")快去劈柴\"");44}45/**46* 小和尚去扎马步(练功夫)47*/48public void kfu(Monk monk,int i){49 System.out.println("(第"+i+"次)"+this.managerName+"说:\"让小和尚("+monk.getSelfName()+")快去扎马步\"");50}51/**52* 小和尚来听故事53*/54public 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 -4-24 下午1:10:14 7 */ 8 public class ThreadStart{ 9/**10* 启动项11* @throws InterruptedException 12*/13public 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*/ 4public 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*/ 4public 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*/22public void cook(Monk monk,int i){23 System.out.println("(第"+i+"次)"+this.managerName+"说:\"让小和尚("+monk.getSelfName()+")快去做饭\"");24}25/**26* 小和尚去劈柴27*/28public 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*/46public 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块的代码是不会相互影响的。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。