synchronized到底锁的是谁、何时生效
创始人
2024-01-17 16:07:22
0

一、synchronized锁的几种形式

  1. synchronized修饰普通方法
  2. synchronized修饰静态方法
  3. synchronized修饰代码块

1、synchronized修饰普通方法

简单示例

public class Test{private String age;private String name;public synchronized void print(String arg1, String arg2) {this.age = this.age + arg1;this.name = this.name + arg2;}
}

下面看一个稍微复杂的场景,main方法中启动两个线程,func1和func2均被synchronized修饰。
由于是非静态方法,锁的是当前对象data,由于func1和func2方法都被synchronized修饰,且在main方法中,是用的new出来的同一个data进行调用,于是锁的就是这个data,用到data发生资源抢占,需要排队。代码首先执行到func1,func1执行3秒,func2排队等待三秒后,执行func2。

public class Test{public static void main(String[] args) {Data data = new Data();new Thread(data::func1).start();new Thread(data::func2).start();}
}class Data{SimpleDateFormat sbf = new SimpleDateFormat("HH:mm:ss");public synchronized void func1(){System.out.println("func1开始执行,当前时间"+ sbf.format(new Date()));try {System.out.println("func1休息3秒,当前时间"+sbf.format(new Date()));Thread.sleep(3000);System.out.println("func1休息3秒结束,当前时间"+sbf.format(new Date()));} catch (InterruptedException e) {e.printStackTrace();}System.out.println("func1:1111111111111111111111111111111111111111111111111");System.out.println("func1执行结束,当前时间"+ sbf.format(new Date()));}public synchronized void func2(){System.out.println("func2开始执行,当前时间"+ sbf.format(new Date()));System.out.println("func2:22222222222222222222222222222222222222222222222222");System.out.println("func2执行结束,当前时间"+ sbf.format(new Date()));}
}

在这里插入图片描述

但如果把func2方法的synchronized修饰去掉,那么运行结果如下。
因为去掉synchronized的func2不再参与data资源的抢占,main方法启动后,func的线程和func2的线程同时启动,首先到达线程1,但因为需要等待3秒,于是func2的结果率先输出,3秒后再输出func1的结果

public class Test{public static void main(String[] args) {Data data = new Data();new Thread(data::func1).start();new Thread(data::func2).start();}
}class Data{SimpleDateFormat sbf = new SimpleDateFormat("HH:mm:ss");public synchronized void func1(){System.out.println("func1开始执行,当前时间"+ sbf.format(new Date()));try {System.out.println("func1休息3秒,当前时间"+sbf.format(new Date()));Thread.sleep(3000);System.out.println("func1休息3秒结束,当前时间"+sbf.format(new Date()));} catch (InterruptedException e) {e.printStackTrace();}System.out.println("func1:1111111111111111111111111111111111111111111111111");System.out.println("func1执行结束,当前时间"+ sbf.format(new Date()));}public void func2(){System.out.println("func2开始执行,当前时间"+ sbf.format(new Date()));System.out.println("func2:22222222222222222222222222222222222222222222222222");System.out.println("func2执行结束,当前时间"+ sbf.format(new Date()));}
}

在这里插入图片描述

此时再做一个变化,func1和func2仍然使用synchronized修饰,但main方法中调用func1与func2每次都是新new出来的Data对象,因为普通方法锁定的是同一个对象,而不同的对象直接不发生资源抢占,因此func2无需等待func1执行完毕再执行,而是和func1同步执行。

public class Test{public static void main(String[] args) {new Thread(new Data()::func1).start();new Thread(new Data()::func2).start();}
}class Data{SimpleDateFormat sbf = new SimpleDateFormat("HH:mm:ss");public synchronized void func1(){System.out.println("func1开始执行,当前时间"+ sbf.format(new Date()));try {System.out.println("func1休息3秒,当前时间"+sbf.format(new Date()));Thread.sleep(3000);System.out.println("func1休息3秒结束,当前时间"+sbf.format(new Date()));} catch (InterruptedException e) {e.printStackTrace();}System.out.println("func1:1111111111111111111111111111111111111111111111111");System.out.println("func1执行结束,当前时间"+ sbf.format(new Date()));}public synchronized void func2(){System.out.println("func2开始执行,当前时间"+ sbf.format(new Date()));System.out.println("func2:22222222222222222222222222222222222222222222222222");System.out.println("func2执行结束,当前时间"+ sbf.format(new Date()));}
}

在这里插入图片描述

2. synchronized修饰静态方法

简单示例

public class Test {public synchronized static void print(String arg1, String arg2) {System.out.println("静态方法锁");}
}

继续使用标题1中的例子,将func1及func2都修改为静态方法,然后在main方法中,new出来两个data对象分别调用,看下面的执行结果,发现虽然是两个对象,但仍然发生了资源抢占,因此synchronized修饰静态方法,锁定是当前类而不是当前对象。

同样的,去掉其中一个方法的synchronized修饰,则不会进行排队。

public class Test {public static void main(String[] args)  {new Thread(()->new Data().func1()).start();new Thread(()->new Data().func2()).start();}
}class Data {static SimpleDateFormat sbf = new SimpleDateFormat("HH:mm:ss");public synchronized static void func1() {System.out.println("func1开始执行,当前时间" + sbf.format(new Date()));try {System.out.println("func1休息3秒,当前时间" + sbf.format(new Date()));Thread.sleep(3000);System.out.println("func1休息3秒结束,当前时间" + sbf.format(new Date()));} catch (InterruptedException e) {e.printStackTrace();}System.out.println("func1:1111111111111111111111111111111111111111111111111");System.out.println("func1执行结束,当前时间" + sbf.format(new Date()));}public synchronized static void func2() {System.out.println("func2开始执行,当前时间" + sbf.format(new Date()));System.out.println("func2:22222222222222222222222222222222222222222222222222");System.out.println("func2执行结束,当前时间" + sbf.format(new Date()));}
}

在这里插入图片描述

3. synchronized修饰代码块

简单示例

public class Test{private String age;private String name;private Object object = new Object();public void print(String arg1, String arg2) {synchronized (object) {this.age = this.age + arg1;}this.name = this.name + arg2;}
}

继续用标题1中的例子修改,起5个线程,去调用func1,func1调用func2,且将func2包裹在同步代码块中,用synchronized修改,锁定this对象,而this指的就是当前Data对象的实例化对象,因此五个线程均发生了资源抢占,挨个按顺序执行。synchronized同步代码块可圈定最小的锁范围,提高效率。

public class Test {public static void main(String[] args) {Data data = new Data();for (int i = 0; i < 5; i++) {new Thread(data::func1).start();}}
}class Data {static SimpleDateFormat sbf = new SimpleDateFormat("HH:mm:ss");public void func1() {System.out.println("func1开始执行,当前时间" + sbf.format(new Date()) + ",当前线程" + Thread.currentThread());System.out.println("在func2执行之前先做点不需要排队没有并发问题的事情" + ",当前线程" + Thread.currentThread());// 此处锁定this对象synchronized (this) {func2();}System.out.println("func1执行结束,当前时间" + sbf.format(new Date()) + ",当前线程" + Thread.currentThread());}public void func2() {try {// 休息三秒Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("func2:22222222222222222222222222222222222222222222222222,当前时间" + sbf.format(new Date()) + ",当前线程" + Thread.currentThread());}
}

在这里插入图片描述
同样的,若将main方法中调用func1的对象,每次都新new出来一个采用不同的对象,则不会进行排队。

public class Test {public static void main(String[] args) {for (int i = 0; i < 5; i++) {// 每次都是不同的对象new Thread(new Data()::func1).start();}}
}class Data {static SimpleDateFormat sbf = new SimpleDateFormat("HH:mm:ss");public void func1() {System.out.println("func1开始执行,当前时间" + sbf.format(new Date()) + ",当前线程" + Thread.currentThread());System.out.println("在func2执行之前先做点不需要排队没有并发问题的事情" + ",当前线程" + Thread.currentThread());// 此处锁定this对象synchronized (this) {func2();}System.out.println("func1执行结束,当前时间" + sbf.format(new Date()) + ",当前线程" + Thread.currentThread());}public void func2() {try {// 休息三秒Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("func2:22222222222222222222222222222222222222222222222222,当前时间" + sbf.format(new Date()) + ",当前线程" + Thread.currentThread());}
}

在这里插入图片描述
但将锁定对象换为new InstanceTest(),也没有触发排队,因为每次锁的对象都是新new出来的,锁的不是同一个,虽然调用的对象是同一个。

public class Test {public static void main(String[] args) {// 调用是同一个对象Data data = new Data();for (int i = 0; i < 5; i++) {new Thread(data::func1).start();}}
}class Data {SimpleDateFormat sbf = new SimpleDateFormat("HH:mm:ss");public void func1() {System.out.println("func1开始执行,当前时间" + sbf.format(new Date()) + ",当前线程" + Thread.currentThread());System.out.println("在func2执行之前先做点不需要排队没有并发问题的事情" + ",当前线程" + Thread.currentThread());// 此处锁定new InstanceTest()对象synchronized (new InstanceTest()) {func2();}System.out.println("func1执行结束,当前时间" + sbf.format(new Date()) + ",当前线程" + Thread.currentThread());}public void func2() {try {// 休息三秒Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("func2:22222222222222222222222222222222222222222222222222,当前时间" + sbf.format(new Date()) + ",当前线程" + Thread.currentThread());}
}

在这里插入图片描述
将锁定对象InstanceTest移到func1方法外面变成Data类的成员变量:private InstanceTest instanceTest = new InstanceTest();,调用仍然是同一个data对象,因此Data类中new出来的InstanceTest也是同一个对象,因此会发生排队。

public class Test {public static void main(String[] args) {// 调用是同一个对象Data data = new Data();for (int i = 0; i < 5; i++) {new Thread(data::func1).start();}}
}class Data {SimpleDateFormat sbf = new SimpleDateFormat("HH:mm:ss");private InstanceTest instanceTest = new InstanceTest();public void func1() {System.out.println("func1开始执行,当前时间" + sbf.format(new Date()) + ",当前线程" + Thread.currentThread());System.out.println("在func2执行之前先做点不需要排队没有并发问题的事情" + ",当前线程" + Thread.currentThread());// 此处锁定instanceTest对象synchronized (instanceTest) {func2();}System.out.println("func1执行结束,当前时间" + sbf.format(new Date()) + ",当前线程" + Thread.currentThread());}public void func2() {try {// 休息三秒Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("func2:22222222222222222222222222222222222222222222222222,当前时间" + sbf.format(new Date()) + ",当前线程" + Thread.currentThread());}
}

在这里插入图片描述
将锁定的对象换为Integer xxx = 1;因为Integer拆箱共享常量池,只有一份,则发生排队

public class Test {public static void main(String[] args) {// 调用是同一个对象Data data = new Data();for (int i = 0; i < 5; i++) {new Thread(data::func1).start();}}
}class Data {SimpleDateFormat sbf = new SimpleDateFormat("HH:mm:ss");public void func1() {System.out.println("func1开始执行,当前时间" + sbf.format(new Date()) + ",当前线程" + Thread.currentThread());System.out.println("在func2执行之前先做点不需要排队没有并发问题的事情" + ",当前线程" + Thread.currentThread());// 此处锁定Integer instanceTest = 1对象Integer instanceTest = 1;synchronized (instanceTest) {func2();}System.out.println("func1执行结束,当前时间" + sbf.format(new Date()) + ",当前线程" + Thread.currentThread());}public void func2() {try {// 休息三秒Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("func2:22222222222222222222222222222222222222222222222222,当前时间" + sbf.format(new Date()) + ",当前线程" + Thread.currentThread());}
}

在这里插入图片描述
但如果将Integer new出来对象,产生了多个不同的Integer对象,就不会排队

public class Test {public static void main(String[] args) {// 调用是同一个对象Data data = new Data();for (int i = 0; i < 5; i++) {new Thread(data::func1).start();}}
}class Data {SimpleDateFormat sbf = new SimpleDateFormat("HH:mm:ss");public void func1() {System.out.println("func1开始执行,当前时间" + sbf.format(new Date()) + ",当前线程" + Thread.currentThread());System.out.println("在func2执行之前先做点不需要排队没有并发问题的事情" + ",当前线程" + Thread.currentThread());// 此处锁定new Integer()对象Integer instanceTest = new Integer(1);synchronized (instanceTest) {func2();}System.out.println("func1执行结束,当前时间" + sbf.format(new Date()) + ",当前线程" + Thread.currentThread());}public void func2() {try {// 休息三秒Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("func2:22222222222222222222222222222222222222222222222222,当前时间" + sbf.format(new Date()) + ",当前线程" + Thread.currentThread());}
}

在这里插入图片描述
将锁定的Integer instanceTest = 1;修改为Integer instanceTest = 128;不发生排队。因为jdk自动拆箱,–128到127之间的值在内存中会被重复利用,认为是一个对象,而超过这个范围的值将会被加载为多个对象,每来一次new一个Integer。因此未发生排队

public class Test {public static void main(String[] args) {// 调用是同一个对象Data data = new Data();for (int i = 0; i < 5; i++) {new Thread(data::func1).start();}}
}class Data {SimpleDateFormat sbf = new SimpleDateFormat("HH:mm:ss");public void func1() {System.out.println("func1开始执行,当前时间" + sbf.format(new Date()) + ",当前线程" + Thread.currentThread());System.out.println("在func2执行之前先做点不需要排队没有并发问题的事情" + ",当前线程" + Thread.currentThread());// 此处锁定new InstanceTest()对象Integer instanceTest = 128;synchronized (instanceTest) {func2();}System.out.println("func1执行结束,当前时间" + sbf.format(new Date()) + ",当前线程" + Thread.currentThread());}public void func2() {try {// 休息三秒Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("func2:22222222222222222222222222222222222222222222222222,当前时间" + sbf.format(new Date()) + ",当前线程" + Thread.currentThread());}
}

在这里插入图片描述

此时我锁类。因为类只有一个,因此会发生排队

public class Test {public static void main(String[] args) {// 调用是同一个对象Data data = new Data();for (int i = 0; i < 5; i++) {new Thread(data::func1).start();}}
}class Data {SimpleDateFormat sbf = new SimpleDateFormat("HH:mm:ss");public void func1() {System.out.println("func1开始执行,当前时间" + sbf.format(new Date()) + ",当前线程" + Thread.currentThread());System.out.println("在func2执行之前先做点不需要排队没有并发问题的事情" + ",当前线程" + Thread.currentThread());// 此处锁定new Data()对象synchronized (Data.class) {func2();}System.out.println("func1执行结束,当前时间" + sbf.format(new Date()) + ",当前线程" + Thread.currentThread());}public void func2() {try {// 休息三秒Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("func2:22222222222222222222222222222222222222222222222222,当前时间" + sbf.format(new Date()) + ",当前线程" + Thread.currentThread());}
}

在这里插入图片描述

二、结论

1. synchronized修饰普通方法

锁定的是当前对象,当前方法的调用对象若是new出来的多份,则不发生资源抢占;只有同一个对象调用该方法时,synchronized锁才生效。

2. synchronized修饰静态方法

锁定是当前类,不论是用类调用的该方法,还是用new出来的一个或N个对象调用的该方法,都会发生资源抢占。

3. synchronized修饰代码块

锁定的是括号中的对象或类。可以认为是结合了1与2的优点与方式。括号中的对象若是一份,则synchronized锁生效,否则无效。生效情况如下:

  • 括号中的对象为this,且调用该方法的对象为同一个
  • 不论调用该方法的对象是不是同一个,括号中的对象永远是同一个,比如:Integer xxx = 1;
  • 括号中为类

相关内容

热门资讯

春风化雨润物无声抒情作文(优... 春风化雨润物无声抒情作文 篇一春天的来临,带来了温暖的春风和细雨,仿佛是大自然对万物的滋润和呵护。这...
我的妈妈四年级作文【实用6篇... 我的妈妈四年级作文 篇一我亲爱的妈妈妈妈是我最亲爱的人,她是我的好朋友,也是我的榜样。我非常喜欢和妈...
擦玻璃四年级作文200字【优... 擦玻璃四年级作文200字 篇一擦玻璃今天放学后,我回到家里看到妈妈正在擦玻璃。看到窗户上的灰尘,我忍...
游作文四年级400字【精选6... 游作文四年级400字 篇一我的暑假游记今年暑假,我和爸爸妈妈一起去了杭州旅行。这是我第一次去杭州,我...
我和孙悟空过一天四年级作文4... 我和孙悟空过一天四年级作文400字 篇一我和孙悟空过一天今天我和孙悟空一起度过了一个令人难忘的一天。...
四年级下册语文书词盘(推荐3... 四年级下册语文书词盘 篇一《小兔乖乖》词盘《小兔乖乖》是四年级下册语文书中的一篇课文,通过小兔乖乖的...
四年级下册作文(推荐5篇) 四年级下册作文 篇一:我的暑假生活我的暑假生活非常充实而有趣。虽然疫情的影响让我不能外出旅游,但我在...
游鹰嘴岩记四年级作文400字... 游鹰嘴岩记四年级作文400字 篇一游鹰嘴岩记我和家人去游鹰嘴岩的经历真是太让人难以忘怀了!鹰嘴岩位于...
游横店秦皇宫小学四年级作文4... 游横店秦皇宫小学四年级作文400字 篇一:探秘横店秦皇宫今天,我去横店秦皇宫参观了,真是被这座宫殿深...
我的同学-小学四年级写人作文... 我的同学-小学四年级写人作文 篇一我的同学小明我有一个非常好的同学,他的名字叫小明。小明是一个非常聪...
作文 好记性不如烂笔头【通用... 作文 好记性不如烂笔头 篇一好记性不如烂笔头,这是一句古训,意味着记忆力再好也不如写下来的好处。在我...
小学生作文四年级400字假期... 小学生作文四年级400字假期回顾 篇一我的暑假暑假终于来了,我迫不及待地迈入了一个充满欢乐和挑战的假...
美丽的安家沟四年级作文(精选... 美丽的安家沟四年级作文 篇一安家沟是我家乡一个美丽的小村庄,它位于山脚下,四周环绕着郁郁葱葱的树林,...
四年级童话作文(精选6篇) 四年级童话作文 篇一:《小兔子的冒险之旅》从前有一只可爱的小兔子,它叫小白。小白住在一个美丽的森林里...
小家庭大变化四年级作文【经典... 小家庭大变化四年级作文 篇一四年级的我,经历了一次小家庭的大变化。这个变化发生在我上小学的第一年,让...
小小动物园四年级作文【最新6... 小小动物园四年级作文 篇一我的家乡有一个小小动物园,里面有各种各样的动物,每次我去都会看到很多有趣的...
我的乐园四年级下册作文200... 我的乐园四年级下册作文200字 篇一我的乐园我家的后院是我的乐园,这里有我最喜欢的花草和小动物。每天...
春天的小学四年级作文300字... 春天的小学四年级作文300字 篇一:春天里的花海春天是一个美丽的季节,它给大地披上了五彩斑斓的外衣。...
我学会了洗衣服四年级作文(精... 我学会了洗衣服四年级作文 篇一我学会了洗衣服在我四年级的时候,我学会了洗衣服。这是一个令人兴奋又有趣...
我的朋友四年级作文500字【... 我的朋友四年级作文500字 篇一:快乐的小伙伴我有一个非常好的朋友,他叫小明。小明是我的同班同学,也...