1. 程式人生 > >面試官:請說一下物件鎖和類鎖的區別

面試官:請說一下物件鎖和類鎖的區別

有鎖才有自由

生活中不存在絕對的自由,絕對的自由通常對應的無序和混沌,只有在道德、法律、倫理的約束下的相對自由,才能使人感受到自由。

而在多執行緒程式設計中,鎖是至關重要的,鎖就是道德,就是法律約束,沒有鎖的多執行緒環境將會是混亂的,所有執行緒都在爭奪資源,最後的結果就是導致系統崩潰,而有了鎖之後,多執行緒環境才能穩定高效的工作。

synchronized 關鍵字

synchronized 是我們所說的重量級鎖,所說的重量級是相對於那些自旋鎖(AQS)而言的,比如可重入鎖ReentrantLock。很多人談 synchronized 色變,說它效能差、太重,貌似言之鑿鑿。放在多年前確實如此,但是 Java 1.7、1.8 已經對 synchronized 做了很大優化,其效能和那些輕量級鎖幾乎沒有差距。

所以,我們再程式中其實可以放心的使用它,即使沒有用過,也肯定在一些原始碼裡見過,比如 Netty 中就有很多地方用到了它。

下面開始進入今天的主題,類鎖和例項鎖。看名字就已經很明顯了,類鎖就是所在類上的鎖,例項就是鎖在類例項上的鎖。

例項鎖

類聲明後,我們可以 new 出來很多的例項物件。這時候,每個例項在 JVM 中都有自己的引用地址和堆記憶體空間,這時候,我們就認為這些例項都是獨立的個體,很顯然,在例項上加的鎖和其他的例項就沒有關係,互不影響了。

通常我們使用例項鎖的方式有下面三種:

1、 鎖住實體裡的非靜態變數

非靜態變數是例項自身變數,不會與其他例項共享,所以鎖住實體內宣告的非靜態變數可以實現物件鎖。鎖住同一個變數的方法塊共享同一把鎖。

2、鎖住 this 物件

this 指的是當前物件例項本身,所以,所有使用 synchronized(this)方式的方法都共享同一把鎖。

3、直接鎖非靜態方法

最簡單、最直觀的一種方式,直接加在方法返回型別前。

使用物件鎖的情況,只有使用同一例項的執行緒才會受鎖的影響,多個例項呼叫同一方法也不會受影響。

下面來做個測試,開啟 5 個執行緒,每個執行緒都 new 一個新的例項來分別呼叫上面三種方式的方法,方法完成的動作就是輸出執行緒名稱,然後休眠 10 秒鐘。

public class ObjectLock {

    private Object lock = new Object();

    /**
     * 鎖住非靜態變數
     * @throws InterruptedException
     */
    public void lockObjectField() throws InterruptedException{
        synchronized (lock){
            System.out.println(Thread.currentThread().getName());
            Thread.sleep(10*1000);
        }
    }

    /**
     * 鎖住 this 物件 this 就是當前物件例項
     * @throws InterruptedException
     */
    public void lockThis() throws InterruptedException{
        synchronized (this){
            System.out.println(Thread.currentThread().getName());
            Thread.sleep(10*1000);
        }
    }

    /**
     * 直接鎖住非靜態方法
     * @throws InterruptedException
     */
    public synchronized void methodLock() throws InterruptedException{
        System.out.println(Thread.currentThread().getName());
        Thread.sleep(10*1000);
    }

    public static void main(String[] args){
        for (int i = 0; i < 5; i++) {
            Thread worker = new Thread(new ObjectLockWorker());
            worker.setName("kite-" + i);
            worker.start();
        }
    }

    public static class ObjectLockWorker implements Runnable{
        @Override
        public void run() {
            try {
                ObjectLock objectLock = new ObjectLock();
                // 方式 1
                objectLock.lockObjectField();
                // 方式 2
                //objectLock.lockThis();
                // 方式 3
                //objectLock.methodLock();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

我們預測的結果就是每個執行緒都會立刻輸出執行緒名稱,然後各自休眠 10 秒。

分別呼叫方式1、2、3,效果都是一樣的,我們看到輸出結果和我們預測的是一樣的,5 個執行緒都立即輸出執行緒名,然後等待 10 秒,整個程式退出。

類鎖

類鎖是載入類上的,而類資訊是存在 JVM 方法區的,並且整個 JVM 只有一份,方法區又是所有執行緒共享的,所以類鎖是所有執行緒共享的。

使用類鎖的方式有如下方式:
1、鎖住類中的靜態變數

因為靜態變數和類資訊一樣也是存在方法區的並且整個 JVM 只有一份,所以加在靜態變數上可以達到類鎖的目的。

2、直接在靜態方法上加 synchronized

因為靜態方法同樣也是存在方法區的並且整個 JVM 只有一份,所以加在靜態方法上可以達到類鎖的目的。

3、鎖住 xxx.class

對當前類的 .class 屬性加鎖,可以實現類鎖。

類鎖是所有執行緒共享的鎖,所以同一時刻,只能有一個執行緒使用加了鎖的方法或方法體,不管是不是同一個例項。

下面同樣來做個測試,開啟 5 個執行緒,除了呼叫靜態方法的方式,其他兩種方式中每個執行緒都 new 一個新的例項來分別呼叫,方法內完成的動作就是輸出執行緒名稱,然後休眠 10 秒鐘。

public class ClassLock {

    private static Object lock = new Object();

    /**
     * 鎖住靜態變數
     * @throws InterruptedException
     */
    public void lockStaticObjectField() throws InterruptedException{
        synchronized (lock){
            System.out.println(Thread.currentThread().getName());
            Thread.sleep(10*1000);
        }
    }

    /**
     * 鎖住靜態方法
     * @throws InterruptedException
     */
    public static synchronized void methodLock() throws InterruptedException{
        System.out.println(Thread.currentThread().getName());
        Thread.sleep(10*1000);
    }

    /**
     * 鎖住 xxx.class
     * @throws InterruptedException
     */
    public void lockClass() throws InterruptedException{
        synchronized (ClassLock.class){
            System.out.println(Thread.currentThread().getName());
            Thread.sleep(10*1000);
        }
    }

    public static void main(String[] args){
        for (int i = 0; i < 5; i++) {
            Thread worker = new Thread(new ClassLockWorker());
            worker.setName("kite-" + i);
            worker.start();
        }
    }

    public static class ClassLockWorker implements Runnable{
        @Override
        public void run() {
            try {
                ClassLock classLock = new ClassLock();
                // 方式 1
                classLock.lockStaticObjectField();
                // 方式 2
                //ClassLock.methodLock();
                // 方式 3
                //classLock.lockClass();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

我們預測的結果就是剛開始只有1個執行緒搶到鎖,然後輸出執行緒名,之後等待 10 秒中,之後是下一個搶到鎖的執行緒,輸出執行緒名,然後等待 10 秒。直到最後一個搶到鎖的執行緒,整個過程歷時大約 50 秒。

分別呼叫方式1、2、3,觀察執行結果,和我們預測的是一致的。

怎麼樣,面試再考你物件鎖和類鎖的時候,是不是能從容一點兒了呢。看到這裡的同學請注意了,點個「推薦」再走,沒有看到這裡的同學就不用注意了(這不廢話嗎)

你還可以讀:

Java 中的幾種執行緒池不知道你用對了沒有

從 volatile 說起,可見性和有序性是什麼

我們說的 CAS 自旋鎖是什麼

相關推薦

面試一下物件區別

有鎖才有自由 生活中不存在絕對的自由,絕對的自由通常對應的無序和混沌,只有在道德、法律、倫理的約束下的相對自由,才能使人感受到自由。 而在多執行緒程式設計中,鎖是至關重要的,鎖就是道德,就是法律約束,沒有鎖的多執行緒環境將會是混亂的,所有執行緒都在爭奪資源,最後的結果就是導致系統崩潰,而有了鎖之後,多執行緒環

面試一下Redis主從複製的功能及實現原理

摘要:Redis在主從模式下會有許多問題需要考慮,這裡寫了一些關於redis在多伺服器下的一些問題分析和總結。 Redis單節點存在單點故障問題,為了解決單點問題,一般都需要對redis配置從節點,然後使用哨兵來監聽主節點的存活狀態,如果主節點掛掉,從節點能繼續提供快取功能。主從配置結合哨兵模式能解決單點故障

阿里Java面試別再問我3次握手與4次揮手了!

在面試中,三次握手和四次揮手可以說是問的最頻繁的一個知識點了,我相信大家也都看過很多關於三次握手與四次揮手的文章。  

面試ThreadLocal的應用場景注意事項有哪些?

## 前言 ThreadLocal主要有如下2個作用 1. 保證執行緒安全 2. 線上程級別傳遞變數 ## 保證執行緒安全 最近一個小夥伴把專案中封裝的日期工具類用在多執行緒環境下居然出了問題,來看看怎麼回事吧 日期轉換的一個工具類 ```java public class DateUtil {

面試寫一個你認為比較“完美”的單例

單例模式是保證一個類的例項有且只有一個,在需要控制資源(如資料庫連線池),或資源共享(如有狀態的工具類)的場景中比較適用。如果讓我們寫一個單例實現,估計絕大部分人都覺得自己沒問題,但如果需要實現一個比較完美的單例,可能並沒有你想象中簡單。本文以主人公小雨的一次面試為背景,循序漸進地討論如何實現一個較為“完美”

面試一下你常用的加密演算法

加密演算法我們整體可以分為:可逆加密和不可逆加密,可逆加密又可以分為:對稱加密和非對稱加密。 ## 一、不可逆加密 常見的不可逆加密演算法有`MD5`,`HMAC`,`SHA1`、`SHA-224`、`SHA-256`、`SHA-384`,和`SHA-512`,其中`SHA-224`、`SHA-256`、

面試一下你對DDD的理解?我馬什麼梅?

領域模型(domain model)是對領域內的概念類或現實世界中物件的視覺化表示。領域模型也稱為概念模型、領域物件模型和分析物件模型。 ——《UML和模式應用》 我們在日常開發中,經常針對一些功能點爭論“這個功能不應該我改,應該是你那邊改”,最終被妥協改了之後都改不明白為什麼這個功能要在自己這邊改。區別於傳

面試一下對框架配置載入的理解吧!

❝ 在上期聊了ThinkPHP類的自動載入,如你還不太瞭解可以跟這下文連結去進行檢視。本文會帶你一起解讀ThinkPHP配置檔案。 ❞ 前言 想了很久終於要開始系列文章的編寫了,期望是寫出提升和麵試都可以搞定的系列文章。 當你看到本文時,如果你發現咔咔沒有編寫到的面試熱點問題或者技術難點,期待評論區指出,一起

Java併發程式設計3 —— 物件

synchronized關鍵字作用在同步程式碼塊上,給共享變數“上鎖”可以解決執行緒安全問題。這把“鎖”可以作用在某個物件上,也可以作用在某個類上。 舉個栗子,有個自助銀行,裡面有兩臺ATM機,工作人員可以看到每次存取款之後機器裡鈔票的總金額數。現在有兩個人來存錢,各存50

Java中物件

Java中的鎖:(簡要描述) 多執行緒的執行緒同步機制實際上是靠鎖的概念來控制的。 在Java程式執行時環境中,JVM需要對兩類執行緒共享的資料進行協調: 1)儲存在堆中的例項變數 2)儲存在方法區中的類變數 這兩類資料是被所有執行緒共享的。 (程式不需要協調儲存在Jav

方法物件區別

synchronized用來處理多個執行緒同時訪問同一個類的一個程式碼塊、方法,甚至這個類。 (1)修飾程式碼塊時,需要設定一個參考物件作為鎖的物件(物件鎖)。 (2)修飾方法時,預設是當前對線作為鎖的物件。 (3)修飾類時,預設是當前類的Class物件作為鎖的物件。 1、物件鎖

Java的sychronized物件區別

  4. 同步加鎖的是物件,而不是程式碼。因此,如果你的類中有一個同步方法,這個方法可以被兩個不同的執行緒同時執行,只要每個執行緒自己建立一個的該類的例項即可。  5. 不同的物件例項的synchronized方法是不相干擾的。也就是說,其它執行緒照樣可以同時訪問相同類的另一個物件例項中的synchroniz

--自旋、阻塞、可重入、悲觀、樂觀、讀寫、偏向所、輕量級、重量級膨脹、物件

參考:http://blog.csdn.net/a314773862/article/details/54095819 自旋鎖 自旋鎖可以使執行緒在沒有取得鎖的時候,不被掛起,而轉去執行一個空迴圈,(即所謂的自旋,就是自己執行空迴圈),若在若干個空迴圈後,執行緒如果可以獲得

透徹理解 Java synchronized 物件區別

synchronized 加到 static 方法前面是給class 加鎖,即類鎖;而synchronized 加到非靜態方法前面是給物件上鎖。這兩者的區別我用程式碼來演示下: 物件鎖和類鎖是不同的鎖,所以多個執行緒同時執行這2個不同鎖的方法時,是非同步的。

Java物件全面解析(多執行緒synchronized關鍵字)

最近工作有用到一些多執行緒的東西,之前吧,有用到synchronized同步塊,不過是別人怎麼用就跟著用,並沒有搞清楚鎖的概念。最近也是遇到一些問題,不搞清楚鎖的概念,很容易碰壁,甚至有些時候自己連用沒用對都不知道。 今天把一些疑惑都解開了,寫篇文章分享給大家

【BAT面試題系列】面試你了解樂觀悲觀嗎?

次數 catch val util overflow info 基本概念 因此 問題 前言 樂觀鎖和悲觀鎖問題,是出現頻率比較高的面試題。本文將由淺入深,逐步介紹它們的基本概念、實現方式(含實例)、適用場景,以及可能遇到的面試官追問,希望能夠幫助你打動面試官。 目錄

面試能解釋一下javascript中bind、applycall這三個函式的用法嗎

一.前言     不知道大家還記不記得前一篇文章:《面試官:能解釋一下javascript中的this嗎》   那今天這篇文章雖然是介紹javascript中bind、apply和call函式,但是多少也和this有點關聯。   假如在前面那場面試末尾,面試官不依不饒繼續問你javascr

面試說說快速失敗安全失敗是什麼

什麼是快速失敗(fail-fast)和安全失敗(fail-safe)?它們又和什麼內容有關係。以上兩點就是這篇文章的內容,廢話不多話,正文請慢用。 我們都接觸 HashMap、ArrayList 這些集合類,這些在 java.util 包的集合類就都是快速失敗的;而 java.util.concurrent

面試給我說一下你項目中的單點登錄是如何實現的?

分享圖片 .get 監聽 rec 返回 例子 比對 .exe 功能 一、單系統登錄機制 1、http無狀態協議 web應用采用browser/server架構,http作為通信協議。http是無狀態協議,瀏覽器的每一次請求,服務器會獨立處理,不與之前或之後的請求產生關聯,這

面試說說一條查詢sql的執行流程底層原理?

序章 自我介紹 我是一條sql,就是一條長長的字串,不要問我長什麼樣,因為我比較傲嬌。   額~~不是我不