同步是多線程中的重要概念。同步的使用能夠保證在多線程運行的環境中,程序不會產生設計以外的錯誤結果。同步的實現方式有兩種,同步方法和同步塊,這兩種方式都要用到synchronized關鍵字。java
給一個方法增長synchronized修飾符以後就可使它成爲同步方法,這個方法能夠是靜態方法和非靜態方法,可是不能是抽象類的抽象方法,也不能是接口中的接口方法。下面代碼是一個同步方法的示例:多線程
public synchronized void aMethod() { 線程
// do something 設計
} 對象
public static synchronized void anotherMethod() { 接口
// do something 同步
} class
線程在執行同步方法時是具備排它性的。當任意一個線程進入到一個對象的任意一個同步方法時,這個對象的全部同步方法都被鎖定了,在此期間,其餘任何線程都不能訪問這個對象的任意一個同步方法,直到這個線程執行完它所調用的同步方法並從中退出,從而致使它釋放了該對象的同步鎖以後。在一個對象被某個線程鎖定以後,其餘線程是能夠訪問這個對象的全部非同步方法的。程序
同步塊是經過鎖定一個指定的對象,來對同步塊中包含的代碼進行同步;而同步方法是對這個方法塊裏的代碼進行同步,而這種狀況下鎖定的對象就是同步方法所屬的主體對象自身。若是這個方法是靜態同步方法呢?那麼線程鎖定的就不是這個類的對象了,也不是這個類自身,而是這個類對應的java.lang.Class類型的對象。同步方法和同步塊之間的相互制約只限於同一個對象之間,因此靜態同步方法只受它所屬類的其它靜態同步方法的制約,而跟這個類的實例(對象)沒有關係。方法
若是一個對象既有同步方法,又有同步塊,那麼當其中任意一個同步方法或者同步塊被某個線程執行時,這個對象就被鎖定了,其餘線程沒法在此時訪問這個對象的同步方法,也不能執行同步塊。
synchronized 關鍵字用於保護共享數據。請你們注意「共享數據」,你必定要分清哪些數據是共享數據,請看下面的例子:
public class ThreadTest implements Runnable{
public synchronized void run(){
for(int i=0;i<10;i++) {
System.out.print(" " + i);
}
}
public static void main(String[] args) {
Runnable r1 = new ThreadTest(); //也可寫成ThreadTest r1 = new ThreadTest();
Runnable r2 = new ThreadTest();
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
}}
在這個程序中,run()雖然被加上了synchronized 關鍵字,但保護的不是共享數據。由於這個程序中的t1,t2 是兩個對象(r1,r2)的線程。而不一樣的對象的數據是不一樣的,r1,r2 有各自的run()方法,因此輸出結果沒法預知。
synchronized的目的是使同一個對象的多個線程,在某個時刻只有其中的一個線程能夠訪問這個對象的synchronized 數據。每一個對象都有一個「鎖標誌」,當這個對象的一個線程訪問這個對象的某個synchronized 數據時,這個對象的全部被synchronized 修飾的數據將被上鎖(由於「鎖標誌」被當前線程拿走了),只有當前線程訪問完它要訪問的synchronized 數據時,當前線程纔會釋放「鎖標誌」,這樣同一個對象的其它線程纔有機會訪問synchronized 數據。