一、提出疑惑

   上一篇文章中,分析了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类

 package com.jason.synch;
/**
* 多线程学习:synchronized
* @function 创建一个小和尚的类
* @author 小风微凉
* @time 2018-4-24 下午12:51:23
*/
public class Monk {
//小和尚的名字
private String selfName; public Monk(String selfName){
this.selfName=selfName;
}
public String getSelfName() {
return selfName;
}
}

  寺院管理者:MonasteryManager类  

 package com.jason.synch;
/**
* 多线程学习:synchronized
* @function 创建一个寺院管理人的类
* @author 小风微凉
* @time 2018-4-24 下午12:59:48
*/
public class MonasteryManager {
//寺院管理人的名称
private String managerName; public MonasteryManager(String managerName){
this.managerName=managerName;
}
/**
* 小和尚去打水
*/
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 }
/**
* 小和尚去做饭
*/
public void cook(Monk monk,int i){
System.out.println("(第"+i+"次)"+this.managerName+"说:\"让小和尚("+monk.getSelfName()+")快去做饭\"");
}
/**
* 小和尚去劈柴
*/
public void firewood(Monk monk,int i){
System.out.println("(第"+i+"次)"+this.managerName+"说:\"让小和尚("+monk.getSelfName()+")快去劈柴\"");
}
/**
* 小和尚去扎马步(练功夫)
*/
public void kfu(Monk monk,int i){
System.out.println("(第"+i+"次)"+this.managerName+"说:\"让小和尚("+monk.getSelfName()+")快去扎马步\"");
}
/**
* 小和尚来听故事
*/
public void listenStory(Monk monk,int i){
System.out.println("(第"+i+"次)"+this.managerName+"说:\"让小和尚("+monk.getSelfName()+")快来听故事\"");
}
}

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

  执行任务的代码:  

 package com.jason.synch;
/**
* 多线程学习:synchronized
* @function 开始测试
* @author 小风微凉
* @time 2018-4-24 下午1:10:14
*/
public class ThreadStart{
/**
* 启动项
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException { //叫来一个小和尚
final Monk littleMonk=new Monk("库林");
//叫来三个寺院管理人
final MonasteryManager manager_1=new MonasteryManager("善良A");//听故事
final MonasteryManager manager_2=new MonasteryManager("凶恶B");//做饭、打水、劈柴
final MonasteryManager manager_3=new MonasteryManager("武术C");//练功夫
//创建三个任务,让管理者指使小和尚去做
new Thread(new Runnable(){
public void run() {
for(int i=1;i<=3;i++){
manager_1.thrash(littleMonk,i);//小和尚去打水
}
}
},"善良线程").start();
new Thread(new Runnable(){
public void run() {
for(int i=1;i<=3;i++){
manager_2.thrash(littleMonk,i);//小和尚去打水
}
}
},"凶恶线程").start();
new Thread(new Runnable(){
public void run() {
for(int i=1;i<=3;i++){
manager_3.thrash(littleMonk,i);//小和尚去打水
}
}
},"武术线程").start();
}
}

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

*************善良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(小和尚){/**/}

简单观察一下:打水方法:

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

分析知道:由于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){},拿到了小和尚库林的对象锁:

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

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

继续看运行结果:

*************善良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类的方法

 /**
* 小和尚去打水
*/
public void thrash(Monk monk,int i){
System.out.println("*************"+this.managerName+"来了,准备布置打水任务****************");
//锁住小和尚:只有当前的管理者可以让这个小和尚去打水
synchronized(monk){
try {
System.out.println("任务开始:(第"+i+"次)"+this.managerName+"说:\"让小和尚("+monk.getSelfName()+")快去打水\",其他寺院管理者等待中。");
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//小和尚打完水后,其他管理者可以命令这个和尚做其他的事情了
System.out.println("小和尚("+monk.getSelfName()+")完成了"+this.managerName+"命令的打水工作,小和尚闲着");
System.out.println("*************"+this.managerName+"离开,任务结束****************");
}
}
/**
* 小和尚去做饭
*/
public void cook(Monk monk,int i){
System.out.println("(第"+i+"次)"+this.managerName+"说:\"让小和尚("+monk.getSelfName()+")快去做饭\"");
}
/**
* 小和尚去劈柴
*/
public void firewood(Monk monk,int i){
System.out.println("*************"+this.managerName+"来了,准备布置劈柴任务****************");
//锁住小和尚:只有当前的管理者可以让这个小和尚去劈柴
synchronized(monk){
try {
System.out.println("任务开始:(第"+i+"次)"+this.managerName+"说:\"让小和尚("+monk.getSelfName()+")快去劈柴\",其他寺院管理者等待中。");
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//小和尚劈柴后,其他管理者可以命令这个和尚做其他的事情了
System.out.println("小和尚("+monk.getSelfName()+")完成了"+this.managerName+"命令的劈柴工作,小和尚闲着");
System.out.println("*************"+this.managerName+"离开,任务结束****************");
}
}
/**
* 小和尚去扎马步(练功夫)
*/
public void kfu(Monk monk,int i){
System.out.println("(第"+i+"次)"+this.managerName+"说:\"让小和尚("+monk.getSelfName()+")快去扎马步\"");
}

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

 //创建三个任务,让管理者指使小和尚去做
new Thread(new Runnable(){
public void run() {
for(int i=1;i<=3;i++){
manager_1.thrash(littleMonk,i);//小和尚去打水
}
}
},"善良线程").start();
new Thread(new Runnable(){
public void run() {
for(int i=1;i<=3;i++){
manager_2.thrash(littleMonk,i);//小和尚去劈柴
}
}
},"凶恶线程").start();
new Thread(new Runnable(){
public void run() {
for(int i=1;i<=3;i++){
manager_3.thrash(littleMonk,i);//小和尚去扎马步
}
}
},"武术线程").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块的代码是不会相互影响的。
  

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

  1. 多线程学习-基础( 九)线程同步Synchronized关键字

    一.线程同步1.synchronized关键字的作用域有二种:(1)某个对象实例内:synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法(如果 ...

  2. (转载)synchronized代码块

    原文:http://blog.csdn.net/luoweifu/article/details/46613015 作者:luoweifu 转载请标名出处 <编程思想之多线程与多进程(1)——以 ...

  3. “全栈2019”Java多线程第二十一章:同步代码块产生死锁的例子

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  4. Java多线程学习(二)synchronized关键字(2)

    转载请备注地址:https://blog.csdn.net/qq_34337272/article/details/79670775 系列文章传送门: Java多线程学习(一)Java多线程入门 Ja ...

  5. Java多线程学习(二)synchronized关键字(1)

    转载请备注地址: https://blog.csdn.net/qq_34337272/article/details/79655194 Java多线程学习(二)将分为两篇文章介绍synchronize ...

  6. 多线程学习-基础(六)分析wait()-notify()-notifyAll()

    一.理解wait()-notify()-notifyAll()obj.wait()与obj.notify()必须要与synchronized(Obj)一起使用,也就是wait,notify是针对已经获 ...

  7. Java多线程系列 基础篇06 synchronized(同步锁)

    转载 http://www.cnblogs.com/paddix/ 作者:liuxiaopeng http://www.infoq.com/cn/articles/java-se-16-synchro ...

  8. 【面试普通人VS高手系列】讲一下wait和notify这个为什么要在synchronized代码块中?

    一个工作七年的小伙伴,竟然不知道"wait"和"notify"为什么要在Synchronized代码块里面. 好吧,如果屏幕前的你也不知道,请在评论区打上&qu ...

  9. Java多线程系列 基础篇05 synchronized关键字

    1. synchronized原理 在java中,每一个对象有且仅有一个同步锁,所以同步锁是依赖于对象而存在.当我们调用某对象的synchronized方法时,就获取了该对象的同步锁.例如,synch ...

随机推荐

  1. 异步通信rabbitmq——消息重试

    目标: 利用RabbitMQ实现消息重试和失败处理,实现可靠的消费消费.在消息消费异常时,自动延时将消息重试,当重试超过一定次数后,则列为异常消息,等待后续特殊处理. 准备: TTL:Time-To- ...

  2. 在装有windows跟ubuntu的机器上重新安装windows后修复ubuntu的grub

    本文只对没有单独用类似easyBCD这种软件单独设立启动分区的双系统,在重新安装win7之后,因为win7覆盖了ubuntu的grub,导致ubuntu无法启动的问题. (1)不管使用什么方法,首先需 ...

  3. Excel合并计算

    office版本为2013,数据来源:我要自学网,曾贤志老师 计算之前,光标定在空白位置,不要定在数据源. 将汇总的类型居于首列(不可以跨区域选择,可以把不需要汇总的移动到其他列). 要有删除原来数据 ...

  4. flask之基础概念

    [应用]一个 Flask 应用是一个 Flask 类的实例.可以在一个被称为应用工厂的函数内部创建 Flask实例.所有应用相关的配置.注册和其他设置都会在函数内部完成,然后返回这个应用.__init ...

  5. Poj 3253 Fence Repair(哈夫曼树)

    Description Farmer John wants to repair a small length of the fence around the pasture. He measures ...

  6. 手把手教你创建Azure ARM Template

    Azure的ARM模式在中国已经落地了.在ARM模式中,通过ARM的Template批量的创建各种资源是与ASM模式的最大的区别之一.目前Azure ARM的Template数量已经越来越多,更多的客 ...

  7. rtsp/rtp over http

    转载:http://linux-expert.blog.163.com/blog/static/764585292008530912712/ rtsp/rtp over http C->S (g ...

  8. 如何在C#中读写INI文件

    INI文件就是扩展名为"ini"的文件.在Windows系统中,INI文件是很多,最重要的就是"System.ini"."System32.ini&q ...

  9. “百度杯”CTF比赛 十一月场(Misc)

    签到题: 题目提示: 文件在i春秋的ctf2群里,加群下载文件 下载下来之后发现有压缩密码 题目提示有提示解压密码:key:ichunqiumemeda 打开文件,得到flag 签到题2: 点击下载附 ...

  10. react-router4.x 实用例子(路由过渡动画、代码分割)

    react-router4.2.0实用例子 代码分割 官网上面写的代码分割是不支持create-react-app脚手架的,要使用import实现 创建一个bundle.js文件 import { C ...