1. 程式人生 > 其它 >八鎖現象理解鎖

八鎖現象理解鎖

技術標籤:多執行緒java多執行緒面試

接下來以程式碼為例來理解鎖的八種情況:

TimeUtils工具類:API提供的常用工具類,時間處理。

1、以下程式碼是先發簡訊還是先打電話?

package com.yy;

import java.util.concurrent.TimeUnit;

public class Test1 {
        public static void main(String[] args) {

            Phone phone = new Phone();

            new Thread(()->{
                phone.
sendSms(); }).start(); try { TimeUnit.SECONDS.sleep(1); //TimeUtil工具類提供睡眠時間 } catch (InterruptedException e) { e.printStackTrace(); } new Thread(()->{ phone.call(); }).start()
; } } class Phone{ public synchronized void sendSms(){ System.out.println("發簡訊"); } public synchronized void call(){ System.out.println("打電話"); } }

這時候是先發簡訊還是先打電話?
在這裡插入圖片描述

答案是先發簡訊, 但是我們要記住,這裡不是因為hone.sendSms()先執行,而是鎖的原因。

2、以下程式碼是先發簡訊還是先打電話?

package com.yy;

import java.util.concurrent.TimeUnit;

public class Test1 {
        public static void main(String[] args) {

            Phone phone = new Phone();

            new Thread(()->{
                phone.sendSms();
            }).start();

            try {
                TimeUnit.SECONDS.sleep(1);   //TimeUtil工具類提供睡眠時間
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            new Thread(()->{
                phone.call();
            }).start();
        }
}


class Phone{

    public synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("發簡訊");
    }

    public synchronized void call(){
        System.out.println("打電話");
    }


}

答案依然是先發資訊再打電話。

在這裡插入圖片描述
以上兩個問題說明了不是因為執行的順序原因而是鎖的問題。
為什麼是發簡訊先執行呢?原因是Synchrinized鎖的物件是方法的呼叫者,這裡是phone,兩個方式使用的是同一把鎖,誰先拿到就會誰來使用,發簡訊的方法先拿到鎖,所以先進行發簡訊。

問題3:增加一個普通方法並呼叫,是先打印發簡訊還是列印hello?

package com.yy;

import java.util.concurrent.TimeUnit;

public class Test1 {
        public static void main(String[] args) {

            Phone phone = new Phone();

            new Thread(()->{
                phone.sendSms();
            }).start();

            try {
                TimeUnit.SECONDS.sleep(1);   //TimeUtil工具類提供睡眠時間
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            new Thread(()->{
                phone.hello();
            }).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("打電話");
    }
    
    public void hello(){
        System.out.println("Hello");
    }


}

先看結果:
在這裡插入圖片描述
為什麼呢?原因是因為hello方法不是同步方法,不受鎖的影響。

問題4:兩個物件,兩個同步方法,列印結果是什麼樣的?

package com.yy;

import java.util.concurrent.TimeUnit;

public class Test1 {
        public static void main(String[] args) {

            Phone phone = new Phone();
            Phone phone2 = new Phone();

            new Thread(()->{
                phone.sendSms();
            }).start();

            try {
                TimeUnit.SECONDS.sleep(1);   //TimeUtil工具類提供睡眠時間
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            new Thread(()->{
                phone2.call();
            }).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("打電話");
    }

    public void hello(){
        System.out.println("Hello");
    }


}

結果:
在這裡插入圖片描述
兩個鎖物件判斷誰先執行是根據物件呼叫的時間。

問題5:同步方法是靜態方法一下程式碼列印的結果是?

package com.yy;

import java.util.concurrent.TimeUnit;

public class Test1 {
        public static void main(String[] args) {

            Phone phone = new Phone();

            new Thread(()->{
                phone.sendSms();
            }).start();

            try {
                TimeUnit.SECONDS.sleep(1);   //TimeUtil工具類提供睡眠時間
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            new Thread(()->{
                phone.call();
            }).start();
        }
}


class Phone{

    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,發簡訊先把呼叫。

問題6:兩個物件呼叫兩個靜態同步方法,以下程式碼的結果?

package com.yy;

import java.util.concurrent.TimeUnit;

public class Test1 {
        public static void main(String[] args) {

            Phone phone = new Phone();
            Phone phone2 = new Phone();

            new Thread(()->{
                phone.sendSms();
            }).start();

            try {
                TimeUnit.SECONDS.sleep(1);   //TimeUtil工具類提供睡眠時間
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            new Thread(()->{
                phone2.call();
            }).start();
        }
}


class Phone{

    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("打電話");
    }



}

看結果:
在這裡插入圖片描述
為什麼呢?原因還是因為這時候的鎖物件還是Class物件,Phone和Phone2物件的Class是一樣的,所以在呼叫的時候先打印發簡訊。

問題7:一個靜態同步方法,一個普通方法,一個物件,一下程式碼的結果是?

package com.yy;

import java.util.concurrent.TimeUnit;

public class Test1 {
        public static void main(String[] args) {

            Phone phone = new Phone();

            new Thread(()->{
                phone.sendSms();
            }).start();

            try {
                TimeUnit.SECONDS.sleep(1);   //TimeUtil工具類提供睡眠時間
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            new Thread(()->{
                phone.call();
            }).start();
        }
}


class Phone{

    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("打電話");
    }



}

看結果:
在這裡插入圖片描述
解析:這時候鎖的物件是兩個,一個是Class模板,一個是Phone。屬於兩把鎖,不需要相互去等待,完全可以去根據時間來看結果。

問題8:兩個物件,一個靜態同步方法一個普通同步方法,以下程式碼的結果?

package com.yy;

import java.util.concurrent.TimeUnit;

public class Test1 {
        public static void main(String[] args) {

            Phone phone = new Phone();
            Phone phone2 = new Phone();

            new Thread(()->{
                phone.sendSms();
            }).start();

            try {
                TimeUnit.SECONDS.sleep(1);   //TimeUtil工具類提供睡眠時間
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            new Thread(()->{
                phone2.call();
            }).start();
        }
}


class Phone{

    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("打電話");
    }



}

結果:
在這裡插入圖片描述
原因還是因為程式中存在兩把鎖,鎖的物件有兩個,一個是Class模板,一個是Phone2。鎖的物件不同,相互之間不需要等待。


總結:通過以上內容我們可以知道static鎖的是this的Class模板,而普通的同步方法鎖的是方法呼叫者,如果存在兩把鎖,呼叫之間是不衝突的。