2013年7月20日 星期六

多執行緒程式設計-同步(synchronization)

多執行緒程式設計-同步(synchronization)

Image(13)


Monitor的概念
  • 當兩個以上的執行緒需要存取共同的資源時,需要以某種方式來確定同一時間只有一個執行緒在使用資源。為了達到這個目的的過程稱之同步
  • monitor的概念(也稱為semaphore)是同步的關鍵:
    • monitor是一種用來當成機鎖(lock)、互斥器(mutex)
    • 某時間裡只有一個執行緒可以持有monitor,當執行緒獲得機鎖(lock)時,我們稱之它進入monitor
    • 其他企圖進入monitor的執行緒必須暫停,直到第一個執行緒離開monitor
    • 這些暫停的執行緒就是正在等待monitor
  • 大多數語言語言本身並不支援同步(例如C/C++),因此若要使用執行緒同步則需要利用作業系統的基本指令。
  • Java透過語言元件來實作同步,所以大部分與同步有關的複雜工作也跟著消除了

使用同步方法
  • Java中的同步化很容易,因為所有的物件都有它自己的隱式monitor
  • 若要進入monitor只需要呼叫已經用synchronized關鍵字修飾過的方法即可
    • 當某個執行緒已經進入同步方法時,其他所有試著呼叫相同實體上同步方法(包刮其他的同步方法)的執行緒必須等待
    • 若要離開monitor並且讓出物件的控制權給下一個等待的執行緒,只要讓monitor的持有者從同步方法回傳即可
  • 沒有同步的程式
  • 輸出結果:
    [Hellow[Synchronized[World
    ]
    ]
    ]
  • 競爭情況(race condition):利用sleep()方法,讓正在call()方法中的執行緒能夠讓出執行權,並切換到另一個執行緒。這將會導致三個訊息字串輸出混和在一起。
  • 因為三個執行緒為了完成方法與彼此互相競爭。使得競爭情況非常難以捉模與難以預料,這可能使得你的程式上次執行是正確的,線次執行卻是錯誤的。
  • 若要修正上述程式,你必須將call()的存取序列化(serialize)
    • 限制每次只有一個執行緒能夠存取此方法
    • 在call()的定義式前面加上synchronized關鍵字即可
    • image
      如此才能得到正確的輸出結果:
      [Hellow]
      [Synchronized]
      [World]
  • 在多執行緒的情況下,每當有一個方法或一群方法需要操作物件的內部狀態時,應該要使用synchronized來避免競爭

synchronized敘述句
  • 假設你想要同步存取某個類別的物件,但此類別並不是為多執行緒存取設計的
    • 也就是說此類別並沒有使用synchronized方法
    • 或者類別並不是你所建立的,而且你無法存取原始程式碼
  • 你可以將此物件的方法之呼叫放在synchronized區塊中:

    synchronized (object ){

    //需要同步的敘述

    }

    • object是想要同步的物件之參考
    • 同步區塊可以確保只有當目前執行緒成功進入object的monitor時,才會呼叫object的方法。
  • 以下是另一種版本,在run()方法裡使用同步區塊
      1: public class Synch1 {
      2:   public static void main(String[] args) {
      3:     Callme target=new Callme();
      4:     Caller2 ob[]={
      5:         new Caller2(target, "Hellow"),
      6:         new Caller2(target, "Synchronized"),
      7:         new Caller2(target, "World")
      8:         };
      9:     try {
     10:       ob[0].t.join();
     11:       ob[1].t.join();
     12:       ob[2].t.join();
     13:     } catch (InterruptedException e) {
     14:       // TODO: handle exception
     15:       System.out.println("Interrupted.");
     16:     }    
     17:   }
     18: 
     19: }
     20: 


  • 將target寫在同步區塊中:
      1: public class Caller2 implements Runnable {
      2: 
      3:   String msg;
      4:   Callme target;
      5:   Thread t;
      6:   public Caller2(Callme targ,String s) {
      7:     target=targ;
      8:     msg=s;
      9:     t=new Thread(this);
     10:     t.start();
     11:   }
     12:   @Override
     13:   public void run() {
     14:     synchronized(target){
     15:     target.call(msg);
     16:     }
     17:     // TODO Auto-generated method stub
     18:     
     19:   }
     20: }

  • 此程式同樣可以產生正確的結果

沒有留言:

張貼留言

此部落格主要作為學習研究、心得分享,歡迎大家討論指教...