Java面試之synchronized 和 static synchronized

 

​面試題:

答案: 

不能

不能

不能

不能

 

正文

 

概述

通過分析這兩個用法的分析,我們可以理解java中鎖的概念。一個是實例鎖(鎖在某一個實例對象上,如果該類是單例,那麼該鎖也具有全局鎖的概念),一個是全局鎖(該鎖針對的是類,無論實例多少個對象,那麼線程都共享該鎖)。實例鎖對應的就是synchronized關鍵字,而類鎖(全局鎖)對應的就是static synchronized(或者是鎖在該類的class或者classloader對象上)。

 

區別

synchronized關鍵字

synchronized作用是對類的當前實例(對象)加鎖。可以使用synchronized關鍵字來標記一個方法或者代碼塊,當某個線程調用該對象的synchronized方法或者訪問synchronized代碼塊時,這個線程便獲得了該對象的鎖,其他線程暫時無法訪問這個方法,只有等待這個方法執行完畢或者代碼塊執行完畢,這個線程纔會釋放該對象的鎖(Java 併發編程)。

synchronized代碼塊【synchronized(synObject)】使用起來比synchronized方法要靈活得多。因爲也許一個方法中只有一部分代碼只需要同步,如果此時對整個方法用synchronized進行同步,會影響程序執行效率。而使用synchronized代碼塊就可以避免這個問題(同步對象或類屬性),synchronized代碼塊可以實現只對需要同步的地方進行同步。

與Lock的區別:1. synchronized是Java語言的關鍵字,因此是內置特性,Lock是一個類(java.util.concurrent.locks包),通過這個類可以實現同步訪問;2. synchronized不需要用戶去手動釋放鎖,當synchronized方法或者synchronized代碼塊執行完之後,系統會自動讓線程釋放對鎖的佔用。Lock則必須要用戶去手動釋放鎖,如果沒有主動釋放鎖,就有可能導致出現死鎖現象。

static synchronized:

每個類有一個鎖,它可以用來控制對static數據成員的併發訪問。訪問static synchronized方法佔用的是類鎖,而訪問非static synchronized方法佔用的是對象鎖。

static synchronized控制類的所有實例(對象)的訪問(相應代碼塊)。synchronized相當於 this.synchronized,static synchronized相當於Something.synchronized

 

一個日本作者-結成浩的《java多線程設計模式》有這樣的一個列子:

    pulbic class Something(){   
public synchronized void isSyncA(){}

public synchronized void isSyncB(){}

public static synchronized void cSyncA(){}

public static synchronized void cSyncB(){}

}

那麼,假如有Something類的兩個實例x與y,那麼下列各組方法被多線程同時訪問的情況是怎樣的?

a. x.isSyncA()與x.isSyncB()
b. x.isSyncA()與y.isSyncA()
c. x.cSyncA()與y.cSyncB()
d. x.isSyncA()與Something.cSyncA()

這裏,很清楚的可以判斷:

  1. 都是對同一個實例(x)的synchronized域訪問,因此不能被同時訪問。(多線程中訪問x的不同synchronized域不能同時訪問)

    如果在多個線程中訪問x.isSyncA(),因爲仍然是對同一個實例,且對同一個方法加鎖,所以多個線程中也不能同時訪問。(多線程中訪問x的同一個synchronized域不能同時訪問)

  2. 是針對不同實例的,因此可以同時被訪問(對象鎖對於不同的對象實例沒有鎖的約束)

  3. 因爲是static synchronized,所以不同實例之間仍然會被限制,相當於Something.isSyncA()與 Something.isSyncB()了,因此不能被同時訪問。

  4. 書上的 答案是可以被同時訪問的,答案理由是synchronzied的是實例方法與synchronzied的類方法由於鎖定(lock)不同的原因。

個人分析也就是synchronized 與static synchronized 相當於兩幫派,各自管各自,相互之間就無約束了,可以被同時訪問。

舉個例子:

public class TestSynchronized {
    public synchronized void test1() {
        int i = 5;
        while (i-- > 0) {
            System.out.println(Thread.currentThread().getName() + " : " + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException ie) {
            }
        }
    }

    public static synchronized void test2() {
        int i = 5;
        while (i-- > 0) {
            System.out.println(Thread.currentThread().getName() + " : " + i);
            try {
                Thread.sleep(500);
            } catch (InterruptedException ie) {
            }
        }
    }

    public static void main(String[] args) {
        final TestSynchronized myt2 = new TestSynchronized();
        Thread test1 = new Thread(new Runnable() {
            public void run() {
                myt2.test1();
            }
        }, "test1");
        Thread test2 = new Thread(new Runnable() {
            public void run() {
                TestSynchronized.test2();
            }
        }, "test2");
        test1.start();
        test2.start();
    }
} 
test1 : 4
test2 : 4
test1 : 3
test2 : 3
test2 : 2
test1 : 2
test2 : 1
test1 : 1
test1 : 0
test2 : 0

 

上面代碼synchronized同時修飾靜態方法和實例方法,但是運行結果是交替進行的,這證明了類鎖和對象鎖是兩個不一樣的鎖,控制着不同的區域,它們是互不干擾的。同樣,線程獲得對象鎖的同時,也可以獲得該類鎖,即同時獲得兩個鎖,這是允許的。

結論

A: synchronized static是某個類的範圍,synchronized static cSync{}防止多個線程中多個實例同時訪問這個 類中的synchronized static 方法。它可以對類的所有對象實例起作用。

B: synchronized 是某實例的範圍,synchronized isSync(){}防止多個線程中這一個實例同時訪問這個類的synchronized 方法。

其實總結起來很簡單。

  • 一個鎖的是類對象,一個鎖的是實例對象。

  • 若類對象被lock,則類對象的所有同步方法全被lock;

  • 若實例對象被lock,則該實例對象的所有同步方法全被lock。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章