八锁现象
目录
- 八锁现象
- 哪八种锁?
- 八锁第一锁和第二锁
- 第一锁
- 第二锁
- 第一、二锁解释
- 八锁第三锁
- 第三锁解释
- 八锁第四锁
- 第四锁解释
- 八锁第五锁
- 第五锁解释
- 八锁第六锁
- 第六锁解释
- 八锁第七锁
- 第七锁解释
- 八锁第八锁
- 第八锁解释
- 八锁总结:
羡慕案例中使用sleep方法制造延迟来测试。
学习目标: 什么是锁?锁是什么?如何判断锁的是谁?
哪八种锁?
1、一个对象,俩个同步方法
2、一个对象,俩个同步方法,一个方法延迟
3、两个对象,两个同步方法
4、一个对象,一个同步,一个普通方法
5、一个对象,俩个静态同步方法
6、两个对象,俩个静态同步方法
7、一个对象,一个静态的同步方法,一个同步方法
8、两个对象,一个静态的同步方法,一个同步方法
八锁第一锁和第二锁
第一锁
现象演示
两个线程调用Phone执行Phone下的发短信和打电话方法
1、标准情况下,两个线程先执行发短信,再打电话。
结果:发短信先执行
import java.util.concurrent.TimeUnit;/*** 八锁,就是关于锁的八个问题* 1、标准情况下,两个线程先执行发短信,再打电话*/
public class Test1 {public static void main(String[] args) {Phone phone = new Phone();// 线程A发短信new Thread(()->{phone.sendSms();},"A").start();// 休息一秒try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}// 线程B打电话new Thread(()->{phone.call();},"B").start();}
}
class Phone{// 发短信public synchronized void sendSms(){System.out.println("发短信");}// 打电话public synchronized void call(){System.out.println("打电话");}
}
第二锁
现象演示
2、sendSms方法延时4秒,仍然是先执行发短信,再打电话。所以我们排除执行顺序是按照线程创建的先后顺序
结果:发短信先执行
import java.util.concurrent.TimeUnit;/*** 八锁,就是关于锁的八个问题* 2、sendSms方法延时4秒,仍然是先执行发短信,再打电话*/
public class Test1 {public static void main(String[] args) {Phone phone = new Phone();// 线程A发短信new Thread(()->{phone.sendSms();},"A").start();// 休息一秒try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}// 线程B打电话new Thread(()->{phone.call();},"B").start();}
}
class Phone{// 发短信public synchronized void sendSms(){// 休息四秒try {TimeUnit.SECONDS.sleep(4);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("发短信");}// 打电话public synchronized void call(){System.out.println("打电话");}
}
第一、二锁解释
被synchronized修饰的方法,锁的对象是方法的调用者,此方法中均是对象Phone来调用的,即两个方法调用的对象是同一个、使用的同一把锁,谁先拿到就谁先执行。
八锁第三锁
现象:phone、phone1两个不同对象在两个线程A、B里分别调用发短信、打电话,其中发短信方法有4秒休眠。
结果:打电话先执行
import java.util.concurrent.TimeUnit;/**
也可以使用Phone phone = new Phone();Phone phone2 = new Phone();phone在线程A中调用发短信,phone2在线程B中调用打电话,这里锁的是两个不同的调用者,所以他们是两把锁,所以互不影响,因为发短信中有4秒的线程休眠,故而一定是打电话被先调用*/
public class Test2 {public static void main(String[] args) {Phone2 phone = new Phone2();Phone2 phone1 = new Phone2();/*** 被synchronized 修饰的方式和普通方法 先执行sendSms() 还是 hello()* 答案: hello()* 解释:新增加的这个方法没有 synchronized 修饰,不是同步方法,不受锁的影响!*/// 线程A调用phone发短信new Thread(()->{phone.sendSms();},"A").start();// 休息一秒try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}// 线程B使用phone1调用打电话new Thread(()->{phone1.call();},"B").start();}
}class Phone2{// 发短信public synchronized void sendSms(){// 休息四秒try {TimeUnit.SECONDS.sleep(4);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("发短信");}// 打电话public synchronized void call(){System.out.println("打电话");}
}
第三锁解释
使用Phone phone = new Phone();Phone phone2 = new Phone();phone在线程A中调用发短信,phone2在线程B中调用打电话,这里锁的是两个不同的调用者,所以他们是两把锁,所以互不影响,因为发短信中有4秒的线程休眠,故而一定是打电话被先调用
八锁第四锁
现象:phone对象在线程A中调用被synchronized修饰的发短信方法 和 在线程B中调用未被synchronized修饰的普通方法hello
结果:hello先执行
import java.util.concurrent.TimeUnit;/*** 被synchronized 修饰的方法和普通方法 先执行sendSms() 还是 hello()* 答案: hello()* 解释:新增加的这个方法没有 synchronized 修饰,不是同步方法,不受锁的影响!*/
public class Test2 {public static void main(String[] args) {Phone2 phone = new Phone2();s// 线程A发短信new Thread(()->{phone.sendSms();},"A").start();// 休息一秒try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}// 线程B调用未被synchronized修饰的方法hello()new Thread(()->{phone.hello();},"B").start();}
}class Phone2{// 发短信public synchronized void sendSms(){// 休息四秒try {TimeUnit.SECONDS.sleep(4);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("发短信");}// 打电话public synchronized void call(){System.out.println("打电话");}// say hello 没有被锁 也就是说不是同步方法、不受锁的影响,线程会直接把它来执行public void hello(){System.out.println("hello");}
}
第四锁解释
被synchronized 修饰的方法和普通方法 先执行sendSms() 还是 hello()?
答案是hello()解释:新增加的这个方法没有 synchronized 修饰,不是同步方法,不受锁的影响!
八锁第五锁
现象:Phone3在线程A 和 线程B中分别调用Phone3的被synchronized和static修饰的静态同步方法 发短信 和 打电话。
结果:发信息先调用
import java.util.concurrent.TimeUnit;/*** 将两个同步方法设置为静态*/
public class Test3 {public static void main(String[] args) {// 线程A发短信new Thread(()->{Phone3.sendSms();},"A").start();// 休息一秒try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}// 线程B打电话new Thread(()->{Phone3.call();},"B").start();}
}class Phone3{/*** 在此处我们将 两个同步方法均设为静态方法,此两个方法在类加载是被加载* 所以,在此2个同步方法加载时,它们被全局唯一的Phone3.class对象加载,因为此Phone3.class全局唯一,故而他们被同一个对象加载* 所以此时仍然是发短信先输出*/// 发短信public static synchronized void sendSms(){// 休息四秒try {TimeUnit.SECONDS.sleep(4);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("发短信");}// 打电话public static synchronized void call(){System.out.println("打电话");}
}
第五锁解释
只要方法被 static 修饰,锁的对象就是 Class模板对象,这个则全局唯一!所以说这里是同一个锁,并不是因为synchronized 这里程序会从上往下依次执行
八锁第六锁
现象:phone、phone1两个对象来分别调用两个静态同步方法,哪个先执行?
结论:发短信先执行
import java.util.concurrent.TimeUnit;/***/
public class Test3 {public static void main(String[] args) {// 因为sendSms和call方法均是静态方法,故而Phone3.sendSms()和new Phone3().sendSms()没有区别Phone3 phone = new Phone3();Phone3 phone1 = new Phone3();// 线程A发短信new Thread(()->{phone.sendSms();},"A").start();// 休息一秒try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}// 线程B打电话new Thread(()->{phone1.call();},"B").start();}
}class Phone3{// 发短信public static synchronized void sendSms(){// 休息四秒try {TimeUnit.SECONDS.sleep(4);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("发短信");}// 打电话public static synchronized void call(){System.out.println("打电话");}
}
第六锁解释
/*** 同被static+synchronized 修饰的两个方法,是先发短信还是先打电话()?* 答案:发短信* 解释:只要方法被 static 修饰,锁的对象就是 Class模板对象,这个则全局唯一,class模板只有一个,即使是phone和phone1不是同以次实例化的对象,但是无论 phone.静态方法() 还是 phone1.静态方法() 它们均相当于Phone.静态方法()* 所以说这里是同一个锁,并不是因为synchronized导致。*/
八锁第七锁
现象:被synchronized修饰的普通方法和被synchronized静态方法是先发短信还是先打电话?
结论:先打电话
import java.util.concurrent.TimeUnit;/***/
public class Test3 {public static void main(String[] args) {// 因为sendSms和call方法均是静态方法,故而Phone3.sendSms()和new Phone3().sendSms()没有区别Phone3 phone = new Phone3();// 线程A发短信new Thread(()->{phone.sendSms();},"A").start();// 休息一秒try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}// 线程B打电话new Thread(()->{phone.call();},"B").start();}
}class Phone3{// 发短信public static synchronized void sendSms(){// 休息四秒try {TimeUnit.SECONDS.sleep(4);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("发短信");}// 打电话public synchronized void call(){System.out.println("打电话");}
}
第七锁解释
/*** 被synchronized修饰的普通方法和被synchronized静态方法是先发短信还是先打电话?* 答案:打电话* 解释:只要被static修饰锁的是class模板, 而synchronized 锁的是调用的对象* 这里是两个锁互不影响,按时间先后执行*/
八锁第八锁
现象:phone、phone1两个对象来分别调用被static+synchronized修饰的静态同步方法发短信 和 被synchronized修饰的普通方法打电话,哪个先执行?
结论:随机执行
import java.util.concurrent.TimeUnit;/***/
public class Test3 {public static void main(String[] args) {// 因为sendSms和call方法均是静态方法,故而Phone3.sendSms()和new Phone3().sendSms()没有区别Phone3 phone = new Phone3();Phone3 phone1 = new Phone3();// 线程A发短信new Thread(()->{phone.sendSms();},"A").start();// 休息一秒try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}// 线程B打电话new Thread(()->{phone1.call();},"B").start();}
}class Phone3{// 发短信public static synchronized void sendSms(){// 休息四秒try {TimeUnit.SECONDS.sleep(4);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("发短信");}// 打电话public synchronized void call(){System.out.println("打电话");}
}
第八锁解释
phone、phone1两个对象来分别调用被static+synchronized修饰的静态同步方法发短信 和 被synchronized修饰的普通方法打电话,先执行发短信还是打电话?
答案:打电话
解释: 只要被static 修饰的锁的就是整个class模板
这里一个锁的是class模板 一个锁的是调用者
所以锁的是两个对象,互不影响,按CPU调度顺序执行
八锁总结:
synchronized(Demo.class){ } 和 synchronized(this){ }
1、new this 调用的是这个对象,是一个具体的对象!
2、static class 唯一的一个模板,即Class对象!synchronized 锁的对象是方法的调用者!
- 普通同步方法的调用者是类的实例(实例化对象)
- 静态同步方法的调用者是类的对象(class对象)
在我们编写多线程程序得时候,只需要搞明白这个到底锁的是什么就不会出错了!