在Java中,synchronized代碼塊一次只能由一個線程執(zhí)行。此外,java支持并發(fā)執(zhí)行多個線程。這可能導(dǎo)致兩個或多個線程同時訪問相同的字段或?qū)ο蟆?/p>
同步是保持執(zhí)行中的所有并發(fā)線程同步的過程。同步避免了由于共享內(nèi)存視圖不一致而導(dǎo)致的內(nèi)存一致性錯誤。當(dāng)一個方法被聲明為同步的;線程持有該方法的對象的監(jiān)聽或鎖對象。如果另一個線程正在執(zhí)行同步方法,那么您的線程將被阻塞,直到該線程釋放監(jiān)聽為止。
請注意,我們可以在類中對已定義的方法或塊使用synchronized關(guān)鍵字。synchronized關(guān)鍵字不能與類定義中的變量或?qū)傩砸黄鹗褂谩?/p>
1. Java中的對象級鎖定
對象級鎖是一種機(jī)制,當(dāng)我們想要同步一個非靜態(tài)方法或非靜態(tài)代碼塊時,這樣只有一個線程能夠在類的給定實例上執(zhí)行代碼塊。應(yīng)該始終這樣做,以使實例級數(shù)據(jù)線程安全。
對象級鎖定可以如下所示:
public class DemoClass
{
public synchronized void demoMethod(){}
}
or
public class DemoClass
{
public void demoMethod(){
synchronized (this)
{
//other thread safe code
}
}
}
or
public class DemoClass
{
private final Object lock = new Object();
public void demoMethod(){
synchronized (lock)
{
//other thread safe code
}
}
}
2. Java中的類級別鎖
類級鎖防止多個線程在運行時的任何可用類實例中進(jìn)入同步塊。這意味著如果在運行時有100個DemoClass實例,那么每次只有一個線程能夠在任何一個實例中執(zhí)行demoMethod(),而其他所有實例對于其他線程來講都是被鎖定的。
應(yīng)該始終執(zhí)行類級鎖定,以使靜態(tài)數(shù)據(jù)線程安全。正如我們所知道的,static關(guān)鍵字將方法的數(shù)據(jù)關(guān)聯(lián)到類級別,因此在靜態(tài)字段或方法上使用鎖定將其關(guān)聯(lián)到類級別。
參照下面類級別鎖定的例子:
public class DemoClass
{
//Method is static
public synchronized static void demoMethod(){
}
}
or
public class DemoClass
{
public void demoMethod()
{
//Acquire lock on .class reference
synchronized (DemoClass.class)
{
//other thread safe code
}
}
}
or
public class DemoClass
{
private final static Object lock = new Object();
public void demoMethod()
{
//Lock object is static
synchronized (lock)
{
//other thread safe code
}
}
}
3. 對象級別鎖定與類級別鎖定 - 重要說明
- Java中的同步確保沒有兩個線程可以同時或并發(fā)地執(zhí)行需要相同鎖的同步方法。
-
synchronized關(guān)鍵字只能用于方法和代碼塊。這些方法或塊可以是靜態(tài)的,也可以是非靜態(tài)的。 - 每當(dāng)線程進(jìn)入Java
synchronized方法或塊時,它就獲得一個鎖;每當(dāng)線程離開synchronized方法或塊時,它就釋放鎖。即使線程在完成后或由于任何錯誤或異常離開synchronized方法,也會釋放鎖。 - Java
synchronized關(guān)鍵字本質(zhì)上是可重入的,這意味著如果一個synchronized方法調(diào)用另一個需要相同鎖的synchronized方法,那么持有鎖的當(dāng)前線程可以在不獲取鎖的情況下進(jìn)入該方法。 - 如果同步塊中使用的對象為空,Java同步將拋出
NullPointerException。例如,在上面的代碼示例中,如果鎖初始化為null,synchronized (lock)將拋出NullPointerException。 - Java中的同步方法會給應(yīng)用程序帶來性能損失。所以在絕對需要的時候使用同步。另外,考慮使用同步代碼塊只同步代碼的關(guān)鍵部分。
- 靜態(tài)同步和非靜態(tài)同步方法可能同時或并發(fā)地運行,因為它們鎖定不同的對象。
- 根據(jù)Java語言規(guī)范,不能在構(gòu)造函數(shù)中使用
synchronized關(guān)鍵字。這是非法的,會導(dǎo)致編譯錯誤。 - 不要在Java同步塊的非
final字段上同步。因為非final字段的引用可能隨時改變,不同的線程可能在不同的對象上同步,即根本沒有同步。 - 不要使用
String文字,因為它們可能在應(yīng)用程序的其他地方被引用,并可能導(dǎo)致死鎖。使用new關(guān)鍵字創(chuàng)建的String對象可以安全地使用。但是最好是,創(chuàng)建一個新的私有范圍的對象實例,或者鎖定我們想要保護(hù)的共享變量本身。