java中synchronized同步

synchronized關鍵字是控制java中線程同步的,能夠分爲synchronized塊和synchronized方法兩種狀況。弄懂具體功能及它們的異同仍是頗有必要的。java


一、同步方法ide

新建的測試類:測試

//the synchronized functions are in this class
class Executer {
    public synchronized void execute1() {
        for (int i = 0; i < 3; ++i) {
            System.out.println(i);
        }
    }
    
    public synchronized void execute2() {
        for (int i = 0; i < 3; ++i) {
            System.out.println(i);
        }
    }

}

//thread1, it will invoke the function "execute1()" in Executer
class Thread1 extends Thread {
    private Executer executer;

    public Thread1(Executer executer) {
        this.executer = executer;
    }

    @Override
    public void run() {
    	executer.execute1();/
    }

}

//thread2, it will invoke the function "execute2()" in Executer
class Thread2 extends Thread {
    private Executer executer;

    public Thread2(Executer executer) {
        this.executer = executer;
    }

    @Override
    public void run() {
    	executer.execute2();
    }

}

1.一、當兩個線程訪問同一個對象的同一個同步方法:this

main方法所在類:spa

public class SyncTest {
	public static void main(String[] args) {
        Executer executer = new Executer();//initiate only one instance

        Thread t1 = new Thread1(executer);
        Thread t2 = new Thread1(executer);//t1 and t2 are both instance of Thread1, and they will both invoke the function "execute1()".

        t1.start();
        t2.start();
    }
}

輸出結果顯然是線程

0
1
2
0
1
2

t1先執行execute1()方法,給其上鎖。訪問完畢後解鎖,再由t2去訪問。而若是execute1()不加synchronized方法的話,輸出將會是00 11 22,也就是t1, t2兩個線程同時訪問execute1()方法。如今咱們須要知道這個「鎖」是上在哪裏的。code

1.二、當兩個線程訪問同一個對象的不一樣同步方法:orm

main方法所在類:對象

public class SyncTest2 {
	public static void main(String[] args) {
        Executer executer = new Executer();//initiate only one instance

        Thread t1 = new Thread1(executer);
        Thread t2 = new Thread2(executer);//t1 will invoke the function "execute1()". And t2 will invoke "execute2()".

        t1.start();
        t2.start();
    }
}

結果執行輸出爲:
同步

0
1
2
0
1
2

跟上例的結果同樣。而咱們這裏兩個線程訪問的明明是兩個不一樣的方法。這說明當有線程訪問同步方法(synchronized)時,會給對象上鎖,而不是方法。上鎖期間,其餘線程不能訪問該對象的任何同步方法。直到此線程結束或者拋異常,纔會把鎖釋放。再來進一步研究鎖的級別。

1.三、靜態方法的同步

將測試類做以下修改(將Executer中的方法改成靜態,兩個Thread類不變):

 class Executer {
    public synchronized static void execute1() {//static synchronized 
        for (int i = 0; i < 3; ++i) {
            System.out.println(i);//print i.
        }
    }
    
    public synchronized static void execute2() {//static synchronized 
        for (int i = 0; i < 3; ++i) {
            System.out.println(i);//print i.
        }
    }

}

而後在main方法中如是輸入:

public class SyncTest2 {
	public static void main(String[] args) {
        Executer executer1 = new Executer();
        Executer executer2 = new Executer();//initiate two instances

        Thread t1 = new Thread1(executer1);//invoke execute1()
        Thread t2 = new Thread2(executer2);//invoke execute2()

        t1.start();
        t2.start();
    }
}

輸出結果:

0
1
2
0
1
2

依然同步。這裏咱們建立了兩個對象實例,按理說不一樣線程訪問各自對象的同步方法的時候,兩個線程間應該互不影響。然而,結果卻說明它們之間會有制約關係。問題就出在static關鍵字。被static修飾的方法爲類方法,而非是對象層面的。當方法同時被static synchronized關鍵字修飾,線程訪問此方法時會給這個類上鎖,而不單單是對象。期間其餘線程沒法訪問這個類的全部靜態同步方法。

2.同步塊

其實也能夠將Executer類中的方法以下寫:

class Executer {
    private Object object = new Object();    
    public void execute1() {        
        synchronized (object) {            
            for (int i = 0; i < 3; ++i) {
                System.out.println(i);
            }
        }
    }
       
    public void execute2() {        
        synchronized (object) {            
            for (int i = 0; i < 3; ++i) {
                System.out.println(i);
            }
        }
    }
    
}

實現的效果跟以前的1.2例同樣。原理就是對object對象加上鎖,同時只能有一個線程能夠訪問該方法。

而後如今有這樣一個問題:

public class SyncTest extends Thread {
	private static String str = "";
	private String type = "";
	
	private static void method(String s) {
		synchronized (str) {
		        /************
			//str = s;
			************/
			System.out.println(s);
			while(true){
				
			}
		}
	}
	
	public void method1() {
		method("method1");
	}
	
	public static void staticMethod1() {
		method("staticMethod1");
	}
	
	public void run() {
		if(type.equals("static")) {
			staticMethod1();
		}
		else{
			method1();
		}
	}
	
	public SyncTest(String type) {
		this.type = type;
	}
	
	public static void main(String[] args) {
		SyncTest1 test1 = new SyncTest1("nonstatic");
		SyncTest1 test2 = new SyncTest1("static");
		test1.start();
		test2.start();
	}

}

問註釋那一行加與不加什麼區別?

如下是解開註釋的結果:

method1
staticMethod1

而後陷入while無限循環。

如下是註釋掉的結果:

method1

也是陷入了while無限循環。


其實產生這個差異的主要緣由是synchronized塊裏的object改變了。註釋那行是修改了此object。若是加上這一行,會致使從新產生一個String對象,此方法快的同步性將被破壞,因而出現了兩個方法同時訪問這個「同步塊」的狀況。

順便說一下,同步塊裏的參數只能是對象(object),不能是基本類型的數據(如int,byte)。

因爲同步塊的控制比較細粒度,並且能夠傳this關鍵字進去(至關於當前對象的一個synchronized修飾的方法),因此使用上可能會更普遍一點吧。

相關文章
相關標籤/搜索