天天看點

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模版