Java 同步塊(synchronized block)用來標記方法或者代碼塊是同步的。Java 同步塊用來避免競爭。本文介紹如下內容:this
Java 中的同步塊用 synchronized 標記。同步塊在 Java 中是同步在某個對象上。全部同步在一個對象上的同步塊在同時只能被一個線程進入並執行操做。全部其餘等待進入該同步塊的線程將被阻塞,直到執行該同步塊中的線程退出。spa
有四種不一樣的同步塊:線程
上述同步塊都同步在不一樣對象上。實際須要那種同步塊視具體狀況而定。3d
下面是一個同步的實例方法:code
public synchronized void add(int value){ this.count += value; }
靜態方法同步和實例方法同步方法同樣,也使用 synchronized 關鍵字。Java 靜態方法同步以下示例:對象
public static synchronized void add(int value){ count += value; }
一樣,這裏 synchronized 關鍵字告訴 Java 這個方法是同步的。blog
靜態方法的同步是指同步在該方法所在的類對象上。由於在 Java 虛擬機中一個類只能對應一個類對象,因此同時只容許一個線程執行同一個類中的靜態同步方法。同步
對於不一樣類中的靜態同步方法,一個線程能夠執行每一個類中的靜態同步方法而無需等待。無論類中的那個靜態同步方法被調用,一個類只能由一個線程同時執行。虛擬機
有時你不須要同步整個方法,而是同步方法中的一部分。Java 能夠對方法的一部分進行同步。it
在非同步的 Java 方法中的同步塊的例子以下所示:
public void add(int value){ synchronized(this){ this.count += value; } }
示例使用 Java 同步塊構造器來標記一塊代碼是同步的。該代碼在執行時和同步方法同樣。
注意 Java 同步塊構造器用括號將對象括起來。在上例中,使用了「this」,即爲調用 add 方法的實例自己。在同步構造器中用括號括起來的對象叫作監視器對象。上述代碼使用監視器對象同步,同步實例方法使用調用方法自己的實例做爲監視器對象。
一次只有一個線程可以在同步於同一個監視器對象的 Java 方法內執行。
下面兩個例子都同步他們所調用的實例對象上,所以他們在同步的執行效果上是等效的。
public class MyClass { public synchronized void log1(String msg1, String msg2){ log.writeln(msg1); log.writeln(msg2); } public void log2(String msg1, String msg2){ synchronized(this){ log.writeln(msg1); log.writeln(msg2); } } }
在上例中,每次只有一個線程可以在兩個同步塊中任意一個方法內執行。
若是第二個同步塊不是同步在 this 實例對象上,那麼兩個方法能夠被線程同時執行。
和上面相似,下面是兩個靜態方法同步的例子。這些方法同步在該方法所屬的類對象上。
public class MyClass { public static synchronized void log1(String msg1, String msg2){ log.writeln(msg1); log.writeln(msg2); } public static void log2(String msg1, String msg2){ synchronized(MyClass.class){ log.writeln(msg1); log.writeln(msg2); } } }
這兩個方法不容許同時被線程訪問。
若是第二個同步塊不是同步在 MyClass.class 這個對象上。那麼這兩個方法能夠同時被線程訪問。
在下面例子中,啓動了兩個線程,都調用 Counter 類同一個實例的 add 方法。由於同步在該方法所屬的實例上,因此同時只能有一個線程訪問該方法。
public class Counter{ long count = 0; public synchronized void add(long value){ this.count += value; } } public class CounterThread extends Thread{ protected Counter counter = null; public CounterThread(Counter counter){ this.counter = counter; } public void run() { for(int i=0; i<10; i++){ counter.add(i); } } } public class Example { public static void main(String[] args){ Counter counter = new Counter(); Thread threadA = new CounterThread(counter); Thread threadB = new CounterThread(counter); threadA.start(); threadB.start(); } }
建立了兩個線程。他們的構造器引用同一個 Counter 實例。Counter.add 方法是同步在實例上,是由於 add 方法是實例方法而且被標記上 synchronized 關鍵字。所以每次只容許一個線程調用該方法。另一個線程必需要等到第一個線程退出 add()方法時,才能繼續執行方法。
若是兩個線程引用了兩個不一樣的 Counter 實例,那麼他們能夠同時調用 add()方法。這些方法調用了不一樣的對象,所以這些方法也就同步在不一樣的對象上。這些方法調用將不會被阻塞。以下面這個例子所示:
public class Example { public static void main(String[] args){ Counter counterA = new Counter(); Counter counterB = new Counter(); Thread threadA = new CounterThread(counterA); Thread threadB = new CounterThread(counterB); threadA.start(); threadB.start(); } }
注意這兩個線程,threadA 和 threadB,再也不引用同一個 counter 實例。CounterA 和 counterB 的 add 方法同步在他們所屬的對象上。調用 counterA 的 add 方法將不會阻塞調用 counterB 的 add 方法。