有了synchronized關鍵字,多線程程序的運行結果將變得可以控制。synchronized關鍵字用于保護共享數據。請大家注意"共享數據",
你一定要分清哪些數據是共享數據,JAVA是面向對象的程序設計語言,所以初學者在編寫多線程程序時,容易分不清哪些數據是共享數據。請看下面的例子:
實例一:
public class FirstThread implements Runnable{
public synchronized void run(){
for(int i=1;i<10;i++){
System.out.println(""+i);
}
}
public FirstThread(){
}
public static void main(String[] args){
Runnable r1=new FirstThread();
Runnable r2=new FirstThread();
Thread t1=new Thread(r1);
Thread t2=new Thread(r2);
t1.start();
t2.start();
}
}
在這個程序中,run()被加上了synchronized關鍵字。在main方法中創建了兩個線程。你可能會認為此程序的運行結果一定為:0123456789 0123456789。答案不是這樣子的,
這個程序中synchronized關鍵字保護的不是共享數據(其實在這個程序中synchronized關
鍵字沒有起到任何作用,此程序的運行結果是不可預先確定的)。這個程序中的t1,t2是兩個對象(r1,r2)的線程。JAVA是面向對象的程序設計語
言,不同的對象的數據是不同的,r1,r2有各自的run()方法,而synchronized使同一個對象的多個線程,在某個時刻只有其中的一個線程可
以訪問這個對象的synchronized數據。每個對象都有一個"鎖標志",當這個對象的一個線程訪問這個對象的某個synchronized數據時,
這個對象的所有被synchronized修飾的數據將被上鎖(因為"鎖標志"被當前線程拿走了),只有當前線程訪問完它要訪問的
synchronized數據時,當前線程才會釋放"鎖標志",這樣同一個對象的其它線程才有機會訪問synchronized數據。
實例二:
public class SecondThread implements Runnable{
public synchronized void run(){
for(int i=1;i<10;i++){
System.out.println(""+i);
}
}
public SecondThread(){
}
public static void main(String[] args){
Runnable r=new SecondThread();
//Runnable r2=new FirstThread();
Thread t1=new Thread(r);
Thread t2=new Thread(r);
t1.start();
t2.start();
}
}
如果你運行1000次這個程序,它的輸出結果也一定每次都是:01234567890123456789。因為這里的synchronized保護的是共享數據。t1,t2是同一個對象(r)的兩個線程,當其中的一個線程(例如:t1)開始執行run()方法時,由于run()受
synchronized保護,所以同一個對象的其他線程(t2)無法訪問synchronized方法(run方法)。只有當t1執行完后t2才有機會
執行。
實例三:
public class ThreeThread implements Runnable{
public void run(){
synchronized(this){
for(int i=1;i<10;i++){
System.out.println(""+i);
}}
}
public ThreeThread(){
}
public static void main(String[] args){
Runnable r=new SecondThree();
//Runnable r2=new FirstThread();
Thread t1=new Thread(r);
Thread t2=new Thread(r);
t1.start();
t2.start();
}
}
這個程序與示例2的運行結果一樣。在可能的情況下,應該把保護范圍縮到最小,可以用示例3的形式,this代表"這個對象"。沒有必要把整個run()保護起來,run()中的代碼只有一個for循環,所以只要保護for循環就可以了。
實例四:
public class FourThread implements Runnable{
public void run(){
for(int i=1;i<10;i++){
System.out.println(Thread.currentThread().getName()+"forloop"+i);
}
synchronized(this){
for(int i=1;i<10;i++){
System.out.println(Thread.currentThread().getName()+"synchronized"+i);
}}
}
public SecondThread(){
}
public static void main(String[] args){
Runnable r=new ThreeThread();
//Runnable r2=new FirstThread();
Thread t1=new Thread(r);
Thread t2=new Thread(r);
t1.start();
t2.start();
}
}
t1_name:forloop:0
t1_name:forloop:1
t1_name:forloop:2
t2_name:forloop:0
t1_name:forloop:3
t2_name:forloop:1
t1_name:forloop:4
t2_name:forloop:2
t1_name:synchronizedforloop:0
t2_name:forloop:3
t1_name:synchronizedforloop:1
t2_name:forloop:4
t1_name:synchronizedforloop:2
t1_name:synchronizedforloop:3
t1_name:synchronizedforloop:4
t2_name:synchronizedforloop:0
t2_name:synchronizedforloop:1
t2_name:synchronizedforloop:2
t2_name:synchronizedforloop:3
t2_name:synchronizedforloop:4
第一個for循環沒有受synchronized保護。對于第一個for循環,t1,t2可以同時訪問。運行結果
表明t1執行到了k=2 時,t2開始執行了。t1首先執行完了第一個for循環,此時還沒有執行完第一個
for循環(t2剛執行到k=2)。t1開始執行第二個for循環,當 t1的第二個for循環執行到k=1時,t2
的第一個for循環執行完了。http://bianceng.cn(編程入門)
t2想開始執行第二個for循環,但由于t1首先執行了第二個for循環,這個對象的鎖標志自然在
t1手中(synchronized方法的執行權也就落到了t1手中),在t1沒執行完第二個for循環的時候,它
是不會釋放鎖標志的。
所以t2必須等到t1執行完第二個for循環后,它才可以執行第二個for循環