Java同步塊(關鍵字: synchronized)
Java同步塊(synchronzied block)是用來標記方法或者程式碼塊同步的。Java同步塊用來避免競爭。本文介紹以下內容:
●Java同步關鍵字(synchronized)
●例項方法同步
●靜態方法同步
●例項方法中同步
●靜態方法中同步
●Java同步例項
Java同步關鍵字(synchronized)
Java中的同步塊使用synchronized標記。同步塊在Java中是同步在某個物件上。所有同步在一個物件上的同步塊同時只能被一個執行緒進入並且執行操作。所有其他等待進入該同步塊的執行緒將被阻塞, 直到執行該同步塊的執行緒退出。
有四種不同的同步塊:
1. 例項方法
2. 靜態方法
3. 例項方法中的同步塊
4. 靜態方法中的同步塊
上述同步塊都作用在不同物件上。實際需要那種同步塊視具體情況而定。
例項同步方法
下面是一個同步的例項方法:
public synchronized void add(int value){
this.count += value;
}
注意在方法宣告中使用同步(synchronized)關鍵字。這告訴Java該方法是同步的。
Java例項方法同步是同步在擁有該方法的物件上。這樣, 每個例項其方法同步都同步在不同物件上, 即改方法所屬的例項。只有一個執行緒能夠在例項方法同步塊中執行。如果有多個例項存在, 那麼一個執行緒一個可以在一個例項同步塊中執行操作。一個例項一個程序。
靜態方法同步
靜態方法同步和例項方法同步一樣, 也使用 synchronized關鍵字。Java靜態方法同步方法如下例項:
public static sysynchronized void add(int value){
this.count += value;
}
同樣, 這裡synchronized關鍵字告訴我們這個方法是同步的。
靜態方法的同步是指同步在該方法所在的類物件上。因為在Java虛擬機器中一個類只能對應一個物件, 所以同時只允許一個執行緒執行同一個類的靜態同步方法。
對於不同類中的靜態同步方法, 一個執行緒可以執行每個類中的靜態同步方法而無需等待。不管類中的那個靜態同步方法被呼叫, 一個類只能由一個執行緒同時執行。
例項方法中的同步塊
又是你不需要同步整個方法, 而是同步方法中的一部風。Java可以對方法中的一部分進行同步。
在非同步的Java方法中的同步塊的列子如下所示:
public void add(int value){
ssynchronized(this){
this.count += value;
}
}
示例中使用Java同步塊構造器來標記程式碼是同步的。改程式碼在執行時和同步方法一樣。
注意Java同步快構造器用括號將物件括起來。在上例中, 使用了"this", 即為呼叫add方法的例項本身。在同步構造器中用括號括起來的物件叫做監視器物件。上述程式碼使用監視器物件同步, 同步例項方法使用呼叫方法本身作為的例項作為監視物件。
一次只有一個執行緒能夠在同步於同一個監視物件的Java方法執行。
下面連個例子都同步他們所呼叫的例項物件上, 因此他們在同步的執行效果是相等的。
public class Main{
public synchronized void log1(String msg1, String msg2){
log.write(msg1);
log.write(msg2);
}
public void log2(Strubg msg1, String msg2){
synchronized(this){
log.write(msg1);
log.write(msg2);
}
}
}
在上例中, 每次只有個一個執行緒能夠在兩個同步塊中任意一個方法執行。
如果第二個同步塊不是同步在this例項物件上, 那麼兩個方法都可以被執行緒同時執行。
靜態方法中的同步塊
和上面類似, 下面是連個靜態方法同步的例子。這些方法同步在所屬類的物件上。
public class Main{
public static synchronized void log1(String msg1, String msg2){
log.write(msg1);
log.write(msg2);
}
public static void log2(Strubg msg1, String msg2){
synchronized(this){
log.write(msg1);
log.write(msg2);
}
}
}
這兩個方法不允許同時被執行緒訪問。
如果第二個同步塊不是同步在Main.class這個物件上, 那麼這兩個方法可以被執行緒同時訪問。
Java同步例項
在下面例子中, 啟動了連個執行緒, 都呼叫Counter類同一個例項都add方法。因為同步在該方法所屬的例項上, 所以同時只能有一個執行緒訪問該方法。
public class Counter{
long count = 0;
public synchronized void add(int value){
this.count += value;
}
}
public class CountThread extend Thread{
protected Counter counter = null;
public CounterThread(Counter counter){
this.counter = counter;
}
public void run(){
for(int i=1; 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方法是例項方法並且標記上synchronize的關鍵字。因此每次只允許一個執行緒呼叫該方法。另外一個執行緒必須要等待第一個執行緒退出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()方法。