synchronized(this) 與synchronized(class) 之間的區(qū)別

一、概念

synchronized 是 Java 中的關鍵字,是利用鎖的機制來實現(xiàn)同步的。

鎖機制有如下兩種特性:

  • 互斥性:即在同一時間只允許一個線程持有某個對象鎖,通過這種特性來實現(xiàn)多線程中的協(xié)調(diào)機制,這樣在同一時間只有一個線程對需同步的代碼塊(復合操作)進行訪問?;コ庑晕覀円餐Q為操作的原子性。

  • 可見性:必須確保在鎖被釋放之前,對共享變量所做的修改,對于隨后獲得該鎖的另一個線程是可見的(即在獲得鎖時應獲得最新共享變量的值),否則另一個線程可能是在本地緩存的某個副本上繼續(xù)操作從而引起不一致。

二、對象鎖和類鎖

1. 對象鎖

在 Java 中,每個對象都會有一個 monitor 對象,這個對象其實就是 Java 對象的鎖,通常會被稱為“內(nèi)置鎖”或“對象鎖”。類的對象可以有多個,所以每個對象有其獨立的對象鎖,互不干擾。

2. 類鎖

在 Java 中,針對每個類也有一個鎖,可以稱為“類鎖”,類鎖實際上是通過對象鎖實現(xiàn)的,即類的 Class 對象鎖。每個類只有一個 Class 對象,所以每個類只有一個類鎖。

三、synchronized 的用法分類

synchronized 的用法可以從兩個維度上面分類:

1. 根據(jù)修飾對象分類

synchronized 可以修飾方法和代碼塊

  • 修飾代碼塊

    • synchronized(this|object) {}

    • synchronized(類.class) {}

  • 修飾方法

    • 修飾非靜態(tài)方法

    • 修飾靜態(tài)方法

2. 根據(jù)獲取的鎖分類

  • 獲取對象鎖

    • synchronized(this|object) {}

    • 修飾非靜態(tài)方法

  • 獲取類鎖

    • synchronized(類.class) {}

    • 修飾靜態(tài)方法,非靜態(tài)方法

四、synchronized 的用法詳解

這里根據(jù)獲取的鎖分類來分析 synchronized 的用法

1、對象鎖

這個對象是新建的,跟其他對象沒有任何關系:

/** * synchronized 修飾非靜態(tài)方法 */
    private void sync5() {
        Log.d(TAG,Thread.currentThread().getName() + "_Sync5: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); **synchronized (new SyncThread())** { try {
                Log.d(TAG, Thread.currentThread().getName() + "_Sync5_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                Thread.sleep(2000);
                Log.d(TAG, Thread.currentThread().getName() + "_Sync5_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

文章后面所有例子都是采用這四個線程,只是對方法進行修改:

SyncThread syncThread = new SyncThread();
        Thread F_thread1 = new Thread(new SyncThread(), "F_thread1");
        Thread F_thread2 = new Thread(new SyncThread(), "F_thread2");
        Thread F_thread3 = new Thread(syncThread, "F_thread3");
        Thread F_thread4 = new Thread(syncThread, "F_thread4");
        F_thread1.start();
        F_thread2.start();
        F_thread3.start();
        F_thread4.start();

運行結果如下:

image.png

四個線程同時開始,同時結束,因為作為鎖的對象與線程是屬于不同的實例。

2、采用類鎖,無所謂哪個類,都會被攔截:

 /** * synchronized 修飾非靜態(tài)方法 */
    private void sync5() {
        Log.d(TAG,Thread.currentThread().getName() + "_Sync5: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); **synchronized (MainActivity.class****)** { try {
                Log.d(TAG, Thread.currentThread().getName() + "_Sync5_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                Thread.sleep(2000);
                Log.d(TAG, Thread.currentThread().getName() + "_Sync5_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

運行結果如下:

image

可以發(fā)現(xiàn),采用類鎖一次只能通過一個。即使采用的是 **MainActivity.class **這個類鎖。

3、采用 this 對象鎖:

/** * synchronized 修飾非靜態(tài)方法 */
    private void sync5() {
        Log.d(TAG,Thread.currentThread().getName() + "_Sync5: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); **synchronized (this****)** { try {
                Log.d(TAG, Thread.currentThread().getName() + "_Sync5_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
                Thread.sleep(2000);
                Log.d(TAG, Thread.currentThread().getName() + "_Sync5_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

運行結果如下:

image

可以發(fā)現(xiàn)線程1,2同時結束,3,4有先后,原因是3,4同屬于一個實例。

4、synchronized 修飾方法

作用范圍是整個方法,所以方法中所有的代碼都是同步的:

/** * synchronized 修飾非靜態(tài)方法 */
    **private synchronized void** **sync5**() {
        Log.d(TAG, Thread.currentThread().getName() + "_Sync5: " + new SimpleDateFormat("HH:mm:ss").format(new Date())); try {
            Log.d(TAG, Thread.currentThread().getName() + "_Sync5_Start: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
            Thread.sleep(2000);
            Log.d(TAG, Thread.currentThread().getName() + "_Sync5_End: " + new SimpleDateFormat("HH:mm:ss").format(new Date()));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

修飾非靜態(tài)方法:

image

對于非靜態(tài)方法,同一個實例的線程訪問會被攔截,非同一實例可以同時訪問。 即此時是默認對象鎖(this)。

修飾靜態(tài)方法結果

image

可以看出來,靜態(tài)方法默認類鎖。

總結

1、對于靜態(tài)方法,由于此時對象還未生成,所以只能采用類鎖;

2、只要采用類鎖,就會攔截所有線程,只能讓一個線程訪問。

3、對于對象鎖(this),如果是同一個實例,就會按順序訪問,但是如果是不同實例,就可以同時訪問。

4、如果對象鎖跟訪問的對象沒有關系,那么就會都同時訪問。

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容