死結 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();
}
}
