天天看点

JUC并发编程——八锁现象JUC并发编程——八锁现象

文章目录

  • JUC并发编程——八锁现象

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("坐公家回家");
    }

}
           

问题一:正常情况下,线程先打印"开车回家"还是"坐公家回家"?

启动程序打印测试:

JUC并发编程——八锁现象JUC并发编程——八锁现象

问题二:car() 同步方法延迟3秒,线程先打印"开车回家"还是"坐公家回家"?

public synchronized void car(){
    try {
        TimeUnit.SECONDS.sleep(3);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("开车回家");
}
           

启动程序打印测试:

JUC并发编程——八锁现象JUC并发编程——八锁现象

解释: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秒,线程先打印"开车回家"还是"坐出租车回家"?

启动程序打印测试:

JUC并发编程——八锁现象JUC并发编程——八锁现象

解释:普通方法没有 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("坐公家回家");
    }
}
           

问题四:两个方法调用者情况下,线程先打印"开车回家"还是"坐公家回家"?

启动程序打印测试:

JUC并发编程——八锁现象JUC并发编程——八锁现象

一秒后打印"坐公家回家",三秒后再打印 “开车回家”

解释:有两个方法调用者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("坐公家回家");
    }
}
           

问题五:静态同步方法下情况下,线程先打印"开车回家"还是"坐公家回家"?

启动程序打印测试:

JUC并发编程——八锁现象JUC并发编程——八锁现象

在案例四的基础上再加一个方法调用者:

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();
           

问题六:静态同步方法下情况下两个方法调用者,线程先打印"开车回家"还是"坐公家回家"?

启动程序打印测试:

JUC并发编程——八锁现象JUC并发编程——八锁现象

解释:加了 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("坐公家回家");
    }
}
           

问题七:一个静态同步方法 + 一个普通同步方法情况下,线程先打印"开车回家"还是"坐公家回家"?

启动程序打印测试:

JUC并发编程——八锁现象JUC并发编程——八锁现象

在案例五的基础上新增一个方法调用者:

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();
           

问题八:一个静态同步方法 + 一个普通同步方法情况下 + 两个方法调用者,线程先打印"开车回家"还是"坐公家回家"?

启动程序打印测试:

JUC并发编程——八锁现象JUC并发编程——八锁现象

解释:一个静态同步方法 + 一个普通同步方法情况下,锁的对象不同,静态同步方法锁的是全局唯一的类模板,普通同步方法锁的是方法的调用者,因此就算有两个方法调用者,锁也不会冲突,所以没有延迟的方法先执行。

总结

synchronized 锁的是方法的调用者,不同的对象对应不同的锁

static 修饰的方法属于Class模版,锁的对象只有一个,就是Class模版