自學java多執行緒(二)------執行緒的同步
1.執行緒同步方式synchronised
為什麼要執行緒同步,舉個簡單的例子,你的銀行賬戶一共有3000塊錢,你的手裡有這3000塊的存摺,你的妻子手裡有這3000塊的銀行卡,你拿著存摺到銀行取2000塊錢,與此同時,你的妻子拿著銀行卡到取款機取2000塊,你們兩個人同時訪問了同一個銀行賬戶,訪問的時候,餘額都是3000塊,當進行取錢操作的時候,系統會判斷你的賬戶裡的錢足夠你和你妻子取出的數目,這樣,賬戶就會同時給你和你妻子一人2000塊,然後賬戶餘額剩下1000,顯然這對銀行來說是虧損,為了避免這個情況,就需要執行緒同步。當你取錢的時候,訪問這個賬戶的餘額的同時給這個賬戶加上一把鎖,在此期間其他人無法訪問這個賬戶,直到這個鎖失效,這樣就達到了執行緒同步的目的。
看下面例子:
package Thread; public class TestSync implements Runnable { Timer timer = new Timer(); public static void main(String[] args) { TestSync test = new TestSync(); Thread thread1 = new Thread(test); Thread thread2 = new Thread(test); thread1.setName("t1"); thread2.setName("t2"); thread1.start(); thread2.start(); } @Override public void run() { timer.addNum(Thread.currentThread().getName()); } } class Timer{ private int num = 0; public void addNum(String name){ num++; try{ Thread.sleep(1); }catch(Exception e){ } System.out.println(name+",你是第"+num+"個訪問timer的執行緒"); } }
輸出結果: t1,你是第2個訪問timer的執行緒
t2,你是第2個訪問timer的執行緒
很明顯這不是我們想要的結果,期待的結果應該是:t1,你是第1個訪問timer的執行緒,t2,你是第2個訪問timer的執行緒。為什麼會出現這種情況呢?當t1執行addNum的時候,num 變成了1,然後程式睡眠了1毫秒,此時t2訪問addNum模組,num再次+1變為2,當t1繼續執行的時候,num就變成了2.如果想要期待的輸出結果,就需要執行緒同步,在上面的addNum方法上加一把鎖。讓addNum方法在同一時間只能有一個執行緒訪問。程式碼如下
class Timer{ private int num = 0; public void addNum(String name){ synchronized (this) { num++; try{ Thread.sleep(1); }catch(Exception e){ } System.out.println(name+",你是第"+num+"個訪問timer的執行緒"); } } }
當t1執行addNum方法時候,synchronized給當前物件上了鎖,其他執行緒只能等到t1訪問結束,才能訪問addNum方法。
輸出結果: t1,你是第1個訪問timer的執行緒
t2,你是第2個訪問timer的執行緒
2.死鎖
簡單的來說就是兩個擁有鎖的資源互相訪問。形成了死鎖。看例子程式
package Thread;
public class TestDeadLock implements Runnable{
public int flag = 1;
static Object o1 = new Object(),o2 = new Object();
@Override
public void run() {
System.out.println("flag=="+flag);
if(flag == 1){
synchronized(o1){
try{
Thread.sleep(500);
}catch(Exception e){}
synchronized(o2){
System.out.println("1");
}
}
}
if(flag == 0){
synchronized(o2){
try{
Thread.sleep(500);
}catch(Exception e){}
synchronized(o1){
System.out.println("0");
}
}
}
}
public static void main(String[] args) {
TestDeadLock test1 = new TestDeadLock();
TestDeadLock test2 = new TestDeadLock();
test1.flag = 1;
test2.flag = 0;
Thread t1 = new Thread(test1);
Thread t2 = new Thread(test2);
t1.start();
t2.start();
}
}
上面程式中兩個執行緒就形成了死鎖,解決的辦法是同步的顆粒度粗一點,不要給物件加鎖,最好給程式碼塊加鎖。至於其他的解決方法也很多,就介紹了。