synchronized(同步鎖)關鍵字是用來解決共享資源的競爭問題。基本上所有的併發模式在解決線程衝突問題時,都是採用序列化訪問共享資源的方案。這意味着在任意時刻,只允許一個任務訪問共享資源。通常這是通過在代碼前加上一條鎖語句來實現的,這就使得在給定時刻,只允許一個任務可以運行這段代碼。因爲鎖語句產生了一種互相排斥的效果,這種機制常常被稱作互斥量。
要控制對共享資源的訪問,得先把它裝進一個對象。然後把所有想訪問這個資源的方法都標記爲synchronized。如果某個任務處於對一個標記爲synchronized的方法的調用中,那麼在這個線程從該方法返回之前,其他所有要調用類中被標記爲synchronized的方法都將處於阻塞狀態。
synchronized void f(){}
synchronized void g(){}
所有對象都自動含有單一的鎖(也稱爲監視器)。
在使用併發時,將域設置爲private是非常重要的,否則,synchronized關鍵字就不能防止其他任務直接訪問域,這將產生衝突。
一個任務可以多次獲得對象的鎖,這表現爲在標記爲synchronized的一個方法中調用另一個標記爲synchronized的方法,甚至可以調用多層。JVM將對調用層數進行計數,直至鎖被完全釋放計數才被置0。
針對每個類,也有一個鎖(作爲類Class對象的一部分),所以synchronized static方法可以在類的範圍內防止對static數據的併發訪問。
每個訪問臨界共享資源的方法都必須被同步,否則它們就不能正確的工作。
public abstract class IntGenerator {
private volatile boolean canceled=false;
public abstract int next();
public void cancel() {canceled=true;}
public boolean isCanceled() {return canceled;}
}
public class EvenChecker implements Runnable{
private IntGenerator generator;
private final int id;
public EvenChecker(IntGenerator g,int ident) {
generator=g;
id=ident;
}
@Override
public void run() {
// TODO Auto-generated method stub
while(!generator.isCanceled()) {
int val=generator.next();
if(val%2!=0) {
System.out.println(val+" is not even");
}
generator.cancel();
}
}
public static void test(IntGenerator gp,int count) {
System.out.println("Press Control-c to exit");
ExecutorService exec=Executors.newCachedThreadPool();
for(int i=0;i<count;i++) {
exec.execute(new EvenChecker(gp,i));
}
exec.shutdown();
}
public static void test(IntGenerator gp) {
test(gp,10);
}
}
public class EvenGenerator extends IntGenerator{
private int currentEvenValue=0;
public synchronized int next() {
++currentEvenValue;
Thread.yield();
++currentEvenValue;
return currentEvenValue;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
EvenChecker.test(new EvenGenerator());
}
}
若去掉EvenGenertor中修飾next()方法的synchronized關鍵字和讓步語句,將導致多個線程執行混亂。即一個線程並不會執行兩次自加操作。