面試題:
答案:
不能
不能
不能
不能
能
正文
概述
通過分析這兩個用法的分析,我們可以理解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()
這裏,很清楚的可以判斷:
-
都是對同一個實例(x)的synchronized域訪問,因此不能被同時訪問。(多線程中訪問x的不同synchronized域不能同時訪問)
如果在多個線程中訪問x.isSyncA(),因爲仍然是對同一個實例,且對同一個方法加鎖,所以多個線程中也不能同時訪問。(多線程中訪問x的同一個synchronized域不能同時訪問)
-
是針對不同實例的,因此可以同時被訪問(對象鎖對於不同的對象實例沒有鎖的約束)
-
因爲是static synchronized,所以不同實例之間仍然會被限制,相當於Something.isSyncA()與 Something.isSyncB()了,因此不能被同時訪問。
-
書上的 答案是可以被同時訪問的,答案理由是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。