synchronized锁的八种情况
概述
记录和理解synchronized锁的八种情况。
定义
一个手机类,三个方法。发消息,发邮件,说你好。前面两者都加了synchronized关键字。
@Data
public class Phone {public synchronized void sendMsg(){System.out.println("send msg");}public synchronized void sendEmail(){System.out.println("send email");}public void sayHello(){System.out.println("hello");}}
第一种情况-普通情况-先发消息,后发邮件
中间加了sleep,保证先发送消息,后发送邮件。
public static void test1() throws Exception {Phone phone = new Phone();new Thread(() -> {phone.sendMsg();}, "a").start();Thread.sleep(1000);new Thread(() -> {phone.sendEmail();}, "b").start();}
第二种情况-发送消息时sleep 4s-先发消息,后发邮件
更改手机的方法,让其在发送消息时先睡4s
@Data
public class Phone {public synchronized void sendMsg() throws InterruptedException {Thread.sleep(4000);System.out.println("send msg");}public synchronized void sendEmail(){System.out.println("send email");}public void sayHello(){System.out.println("hello");}}
测试方法
public static void test2() throws Exception {Phone phone = new Phone();new Thread(() -> {try {phone.sendMsg();} catch (InterruptedException e) {throw new RuntimeException(e);}}, "a").start();Thread.sleep(1000);new Thread(() -> {phone.sendEmail();}, "b").start();}
测试结果
从结果来看,先发消息,后发邮件。虽然说发消息时睡了4s,但还是先发消息。
这是因为加了synchronized锁,锁的是当前的phone对象,由于测试代码中,是先执行sendMsg方法的,所以该方法先拿到了锁资源。所以sendEmail方法要等sendMsg方法释放锁资源后才能获取锁,再执行sendEmail方法。
第三种情况-先说你好,再发消息
phone的方法不变,修改测试方法。
public static void test3() throws Exception {Phone phone = new Phone();new Thread(() -> {try {phone.sendMsg();} catch (InterruptedException e) {throw new RuntimeException(e);}}, "a").start();Thread.sleep(1000);new Thread(() -> {phone.sayHello();}, "b").start();}
测试结果
测试方法中,虽然先调用sendMsg方法,但是其本身睡4s,而且sayHello方法是没有加sychronized锁的,所以当主线程睡了1s后就直接执行sayHello方法了。
第四种情况-两部手机-先发邮件,再发消息
phone类定义不变
测试方法,实例一个新手机,两个手机执行不同方法。
public static void test4() throws Exception {Phone phone1 = new Phone();Phone phone2 = new Phone();new Thread(() -> {try {phone1.sendMsg();} catch (InterruptedException e) {throw new RuntimeException(e);}}, "a").start();Thread.sleep(1000);new Thread(() -> {phone2.sendEmail();}, "b").start();}
测试结果
这是因为synchronized锁的是实例对象,phone1和phone2是两个实例,所以不会影响。
第五种情况-两个静态同步方法,1个手机,先发消息还是发邮件-先发消息,再发邮件
修改phone类
@Data
public class Phone {public static synchronized void sendMsg() throws InterruptedException {Thread.sleep(4000);System.out.println("send msg");}public static synchronized void sendEmail(){System.out.println("send email");}public void sayHello(){System.out.println("hello");}}
测试方法
public static void test5() throws Exception {Phone phone= new Phone();new Thread(() -> {try {phone.sendMsg();} catch (InterruptedException e) {throw new RuntimeException(e);}}, "a").start();Thread.sleep(1000);new Thread(() -> {phone.sendEmail();}, "b").start();}
测试结果
这是因为两个方法加了static关键字,那么synchronized锁的是Phone这个类。所以sendMsg和sendEmail相当于串行执行了。
第六种情况-两个静态同步方法,2个手机,先发消息还是发邮件-先发消息,再发邮件
测试方法
public static void test6() throws Exception {Phone phone1= new Phone();Phone phone2= new Phone();new Thread(() -> {try {phone1.sendMsg();} catch (InterruptedException e) {throw new RuntimeException(e);}}, "a").start();Thread.sleep(1000);new Thread(() -> {phone2.sendEmail();}, "b").start();}
测试结果
其实加了static,synchronzed锁的是这个Phone类,其锁的范围比锁实例时更大。
第七种情况-一个静态方法,一个普通方法,1个手机-先发邮件,再发消息
修改phone类,sendEmail变成普通方法
@Data
public class Phone {public static synchronized void sendMsg() throws InterruptedException {Thread.sleep(4000);System.out.println("send msg");}public synchronized void sendEmail(){System.out.println("send email");}public void sayHello(){System.out.println("hello");}}
测试代码
public static void test7() throws Exception {Phone phone= new Phone();new Thread(() -> {try {phone.sendMsg();} catch (InterruptedException e) {throw new RuntimeException(e);}}, "a").start();Thread.sleep(1000);new Thread(() -> {phone.sendEmail();}, "b").start();}
测试结果
第七第八种情况都说明了锁的范围和对象不同。
static+synchronized 锁的是Class对象。
sychronized 锁的是this对象。
第八种情况-一个静态方法,一个普通方法,2个手机-先发邮件,再发消息
测试代码
public static void test8() throws Exception {Phone phone1= new Phone();Phone phone2= new Phone();new Thread(() -> {try {phone1.sendMsg();} catch (InterruptedException e) {throw new RuntimeException(e);}}, "a").start();Thread.sleep(1000);new Thread(() -> {phone2.sendEmail();}, "b").start();}
测试结果