死結 Deadlock
多個執行緒互等對方的資源, 造成誰也不讓誰的狀況
Starvation 飢餓
synchronized區塊需花費常時間, 而某執行緒頻繁取得此資源, 造成其他執行緒一直block
Livelock
每支程式都很忙碌, 但並不是死結
Exam1-synchronized的用法
不同的人作不同的事,用相同的工具
只有一台車. 有駕駛A及駕駛B分別要去台北及屏東1000次.
如下程式碼, 若沒有synchronized(car), 就會造成如下紅色部份, 駕駛B取車後, 又被駕駛A搶去
駕駛A 取車 : 去台北 : 842
駕駛B 取車 : 駕駛A 取車 : 去台北 : 843
駕駛A 取車 : 去台北 : 844
去屏東 : 493
package threadtest2; public class ThreadTest2 { public static void main(String[] args) { Car car=new Car(); new Thread(new Runnable(){ @Override public void run() { for (int i=0;i<1000;i++){ synchronized(car){ car.drive(); System.out.printf("去台北 : %d\n", i); } } } },"駕駛A").start(); new Thread(new Runnable(){ @Override public void run() { for (int i=0;i<1000;i++){ synchronized(car){ car.drive(); System.out.printf("去屏東 : %d\n", i); } } } },"駕駛B").start(); } } class Car{ public void drive(){ System.out.printf("%s 取車 : ", Thread.currentThread().getName()); } }
Exam2-synchronized
不同的人作相同的事, 用相同的工具
只有一台車. 有駕駛A及駕駛B, 二人共同將貨物運出1000次.
如下程式碼, 若沒有synchronized(car), 就會造成如下紅色部份, 駕駛B取車後, 又被駕駛A搶去. 另一個現在是出車數會重複不正確
駕駛A 取車 : 駕駛B 取車 : 送貨去屏東 : 249
送貨去台北 : 249
駕駛B 取車 : 駕駛A 取車 : 送貨去屏東 : 251
駕駛B 取車 : 送貨去台北 : 251
駕駛A 取車 : 送貨去屏東 : 252
請注意, 不同的執行緒執行相同的Runnable時, Runnable裏的區域變數對每個執行緒都是不一樣的. 也就是說每個執行緒都會copy一份Runnable裏的區域變數, 然後放在自己的stack區. 所以下述的變數 i, 必需宣告為物件變數
package threadtest3; public class ThreadTest3 { public static void main(String[] args) { Deliver deliver=new Deliver(new Car()); Thread t1=new Thread(deliver, "駕駛A"); t1.start(); Thread t2=new Thread(deliver, "駕駛B"); t2.start(); } } class Deliver implements Runnable{ Car car; int i; public Deliver(Car car){ this.car=car; } @Override public void run() { while(true){ synchronized(car){ i++; if(i<=1000){ car.drive(); System.out.printf("%s 送出貨物 : %d\n", Thread.currentThread().getName(),i); } else{ break; } } } } } class Car{ public void drive(){ System.out.printf("%s 取車 : ", Thread.currentThread().getName()); } }
Exam3-wait作用
不同的人作不同的事, 用相同的工具, 且某種特殊條件才可以作
只有一台車, 有二個駕駛, 單次車次是上台北, 雙次車次是下屏東.
wait() 通常是在某種狀況下, 將擁有此物件的執行緒退出等待, 待另一種狀況發生後, 再notify()通知等待的執行緒進入Runnable階段.
wait(), notify()都是物件變數, 所以要由物件來呼叫
所以wait()一定要在synchronized區塊之中. 這點有點怪怪的, 因為一定要在synchronized之中, 代表執行緒擁有此物件鎖, 然後退出等待, 那鎖呢?? 原來wait()時, 執行緒會放棄這把鎖, 所以不會佔著茅坑不拉屎.
package threadtest4; public class ThreadTest4 { public static void main(String[] args) { Car car=new Car(); new Thread(new Runnable(){ @Override public void run() { for(int i=0;i<500;i++){ car.driveToNorth(); } } }, "駕駛A").start(); new Thread(new Runnable(){ @Override public void run() { for(int i=0;i<500;i++){ car.driveToSouth(); } } }, "駕駛B").start(); } } class Car{ int i=1; public synchronized void driveToNorth(){ if(i%2==0){ try { wait(); } catch (InterruptedException ex) { } } System.out.printf("%s 出車去台北 : %d\n", Thread.currentThread().getName(), i); i++; notify(); } public synchronized void driveToSouth(){ if(i%2==1){ try { wait(); } catch (InterruptedException ex) { } } System.out.printf("%s 出車去屏東 : %d\n", Thread.currentThread().getName(), i); i++; notify(); } }