synchronized關鍵字有兩種用法。第一種就是在《使用Synchronized關鍵字同步類別方法》一文中所介紹的直接用在方法的定義中。另外一種就是synchronized塊。我們不僅可以透過synchronized區塊來同步一個物件變數。也可以使用synchronized區塊來同步類別中的靜態方法和非靜態方法。
synchronized區塊的語法如下:
複製代碼代碼如下:
public void method()
{
… …
synchronized(表達式)
{
… …
}
}
一、非靜態類別方法的同步
從《使用Synchronized關鍵字同步類別方法》一文中我們知道使用synchronized關鍵字來定義方法就會鎖定類別中所有使用synchronzied關鍵字定義的靜態方法或非靜態方法,但這並不好理解。而如果使用synchronized塊來達到同樣的效果,就不難理解為什麼會產生這種效果了。如果想要使用synchronized區塊來鎖定類別中所有的同步非靜態方法,需要使用this做為synchronized區塊的參數傳入synchronized區塊國,程式碼如下:
透過synchronized區塊同步非靜態方法
複製代碼代碼如下:
public class SyncBlock
{
public void method1()
{
synchronized(this) // 相當於對method1方法使用synchronized關鍵字
{
… …
}
}
public void method2()
{
synchronized(this) // 相當於對method2方法使用synchronized關鍵字
{
… …
}
}
public synchronized void method3()
{
… …
}
}
在上面的程式碼中的method1和method2方法中使用了synchronized區塊。而第017行的method3方法仍然使用synchronized關鍵字來定義方法。在使用同一個SyncBlock類別實例時,這三個方法只要有一個正在執行,其他兩個方法就會因為未獲得同步鎖定而被阻塞。在使用synchronized區塊時要想達到和synchronized關鍵字同樣的效果,必須將所有的程式碼都寫在synchronized區塊中,否則,將無法使目前方法中的所有程式碼和其他的方法同步。
除了使用this做為synchronized區塊的參數外,還可以使用SyncBlock.this作為synchronized區塊的參數來達到相同的效果。
在內類(InnerClass)的方法中使用synchronized區塊來時,this只表示內類,和外類(OuterClass)沒有關係。但內類別的非靜態方法可以和外類別的非靜態方法同步。如在內類InnerClass中加一個method4方法,並使method4方法和SyncBlock的三個方法同步,程式碼如下:
使內類別的非靜態方法和外類別的非靜態方法同步
複製代碼代碼如下:
public class SyncBlock
{
… …
class InnerClass
{
public void method4()
{
synchronized(SyncBlock.this)
{
… …
}
}
}
… …
}
在上面SyncBlock類別的新版本中,InnerClass類別的method4方法和SyncBlock類別的其他三個方法同步,因此,method1、method2、method3和method4四個方法在同一時間只能有一個方法執行。
Synchronized區塊不管是正常執行完,或是因為程式出錯而異常退出synchronized區塊,目前的synchronized區塊所持有的同步鎖定都會自動釋放。因此,在使用synchronized區塊時不必擔心同步鎖定的釋放問題。
二、靜態類別方法的同步
由於在呼叫靜態方法時,物件實例不一定會被建立。因此,就不能使用this來同步靜態方法,而必須使用Class物件來同步靜態方法。程式碼如下:
透過synchronized塊同步靜態方法
複製代碼代碼如下:
public class StaticSyncBlock
{
public static void method1()
{
synchronized(StaticSyncBlock.class)
{
… …
}
}
public static synchronized void method2()
{
… …
}
}
在同步靜態方法時可以使用類別的靜態欄位class來得到Class物件。在上例中method1和method2方法同時只能有一個方法執行。除了使用class欄位得到Class物件外,還可以使用實例的getClass方法來得到Class物件。上例中的程式碼可以修改如下:
使用getClass方法得到Class對象
複製代碼代碼如下:
public class StaticSyncBlock
{
public static StaticSyncBlock instance;
public StaticSyncBlock()
{
instance = this;
}
public static void method1()
{
synchronized(instance.getClass())
{
}
}
}
在上面程式碼中透過一個public的靜態instance得到一個StaticSyncBlock類別的實例,並且透過這個實例的getClass方法得到了Class物件(一個類別的所有實例透過getClass方法得到的都是同一個Class對象,因此,呼叫任何一個實例的getClass方法都可以)。我們也可以透過Class物件讓不同類別的靜態方法同步,如Test類別的靜態方法method和StaticSyncBlock類別的兩個靜態方法同步,程式碼如下:
Test類別的method方法和StaticSyncBlock類別的method1、method2方法同步
複製代碼代碼如下:
public class Test
{
public static void method()
{
synchronized(StaticSyncBlock.class)
{
}
}
}
注意:在使用synchronized區塊同步類別方法時,非靜態方法可以使用this來同步,而靜態方法必須使用Class物件來同步。它們互不影響。當然,也可以在非靜態方法中使用Class物件來同步靜態方法。但在靜態方法中不能使用this來同步非靜態方法。這一點在使用synchronized塊同步類別方法時應注意。