文章目录
JUC并发编程——八锁现象
八锁现象
八锁现象,其实就是关于锁的八个问题,通过八个问题,逐渐加深对锁的认识,为了方便演示,这里全部用synchronized。
案例一:两个synchronized 同步方法 + 两个线程 + 一个方法调用者
package com.cheng.Lock8;
public class Test1 {
public static void main(String[] args) {
BackHome backHome = new BackHome();
new Thread(()->{
backHome.car();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
backHome.bus();
},"B").start();
}
}
class BackHome{
public synchronized void car(){
System.out.println("开车回家");
}
public synchronized void bus(){
System.out.println("坐公家回家");
}
}
问题一:正常情况下,线程先打印"开车回家"还是"坐公家回家"?
启动程序打印测试:
问题二:car() 同步方法延迟3秒,线程先打印"开车回家"还是"坐公家回家"?
public synchronized void car(){
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("开车回家");
}
启动程序打印测试:
解释:synchronized 锁的对象是方法的调用者,也就是上面的 backHome 对象, backHome 对象只有一个,因此锁也只有一个,两个方法用的是用一把锁,谁先拿到谁就先执行。
案例二:两个同步方法 + 一个普通方法 + 两个线程 + 一个方法调用者
package com.cheng.Lock8;
import java.util.concurrent.TimeUnit;
public class Test2 {
public static void main(String[] args) {
BackHome2 backHome = new BackHome2();
new Thread(()->{
backHome.car();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
backHome.bus();
},"B").start();
new Thread(()->{
backHome.taxi();
},"C").start();
}
}
class BackHome2{
public synchronized void car(){
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("开车回家");
}
public synchronized void bus(){
System.out.println("坐公家回家");
}
//普通方法
public void taxi(){
System.out.println("坐出租车回家");
}
}
问题三:car() 同步方法延迟3秒,线程先打印"开车回家"还是"坐出租车回家"?
启动程序打印测试:
解释:普通方法没有 synchronized 关键字,不受锁的影响,又因为car() 同步方法延迟3秒,所以线程先执行普通方法。
案例三:两个同步方法 + 两个线程 + 两个方法调用者
backHome1 调用 car(),
backHome2 调用 bus()
package com.cheng.Lock8;
import java.util.concurrent.TimeUnit;
public class Test2 {
public static void main(String[] args) {
BackHome2 backHome1 = new BackHome2();
BackHome2 backHome2 = new BackHome2();
new Thread(()->{
backHome1.car();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
backHome2.bus();
},"B").start();
}
}
class BackHome2{
public synchronized void car(){
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("开车回家");
}
public synchronized void bus(){
System.out.println("坐公家回家");
}
}
问题四:两个方法调用者情况下,线程先打印"开车回家"还是"坐公家回家"?
启动程序打印测试:
一秒后打印"坐公家回家",三秒后再打印 “开车回家”
解释:有两个方法调用者backHome1和backHome2,因此两个同步方法有两把锁,而因为car()方法有延迟,所以bus()方法先执行。
案例四:两个静态同步方法 + 两个线程 + 一个方法调用者
package com.cheng.Lock8;
import java.util.concurrent.TimeUnit;
public class Test3 {
public static void main(String[] args) {
BackHome3 backHome = new BackHome3();
new Thread(()->{
backHome.car();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
backHome.bus();
},"B").start();
}
}
class BackHome3{
public static synchronized void car(){
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("开车回家");
}
public static synchronized void bus(){
System.out.println("坐公家回家");
}
}
问题五:静态同步方法下情况下,线程先打印"开车回家"还是"坐公家回家"?
启动程序打印测试:
在案例四的基础上再加一个方法调用者:
BackHome3 backHome = new BackHome3();
BackHome3 backHome1 = new BackHome3();
new Thread(()->{
backHome.car();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
backHome1.bus();
},"B").start();
问题六:静态同步方法下情况下两个方法调用者,线程先打印"开车回家"还是"坐公家回家"?
启动程序打印测试:
解释:加了 static 的同步方法,在类一加载就有了,锁的对象不再是方法的调用者,而是Class的类模板,不管有几个方法调用者,锁的对象都是这个类模板且全局唯一。
案例五:一个静态同步方法 + 一个普通同步方法 + 一个方法调用者
package com.cheng.Lock8;
import java.util.concurrent.TimeUnit;
public class Test4 {
public static void main(String[] args) {
BackHome4 backHome = new BackHome4();
new Thread(()->{
backHome.car();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
backHome.bus();
},"B").start();
}
}
class BackHome4{
public static synchronized void car(){
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("开车回家");
}
public synchronized void bus(){
System.out.println("坐公家回家");
}
}
问题七:一个静态同步方法 + 一个普通同步方法情况下,线程先打印"开车回家"还是"坐公家回家"?
启动程序打印测试:
在案例五的基础上新增一个方法调用者:
BackHome4 backHome = new BackHome4();
BackHome4 backHome1 = new BackHome4();
new Thread(()->{
backHome.car();
},"A").start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
backHome1.bus();
},"B").start();
问题八:一个静态同步方法 + 一个普通同步方法情况下 + 两个方法调用者,线程先打印"开车回家"还是"坐公家回家"?
启动程序打印测试:
解释:一个静态同步方法 + 一个普通同步方法情况下,锁的对象不同,静态同步方法锁的是全局唯一的类模板,普通同步方法锁的是方法的调用者,因此就算有两个方法调用者,锁也不会冲突,所以没有延迟的方法先执行。
总结
对于普通同步方法,锁是当前实例对象
对于静态同步方法,锁是当前类的class对象
对于同步方法块,锁是括号里面的对象