多線程編程之臨界區模式

 臨界區模式 Critical Section Pattern 是指在一個共享範圍中只讓一個線程執行的模式。它是所有其它多線程設計模式的基礎,所以我首先來介紹它。把着眼點放在範圍上,這個模式叫臨界區模式,如果把作眼點放在執行的線程上,這個模式就叫單線程執行模式。

  首先我們來玩一個鑽山洞的遊戲,我 Axman,朋友 Sager,同事 Pentium4.三個人在八角遊樂場循環鑽山洞(KAO,減肥訓練啊),每個人手裏有一個牌子,每鑽一次洞口的老頭會把當前的次序,姓名,牌號顯示出來,並檢查名字與牌號是否一致。

  OK,這個遊戲的參與者有遊樂場老頭Geezer,Player,就是我們,還有山洞 corrie.

  

  public class Geezer {

  public static void main(String[] args){

  System.out.println(預備,開始!);

  Corrie c = new Corrie(); //只有一個山洞,所以生存一個實例後傳給多個Player.

  new Player(Axman,001,c).start();

  new Player(Sager,002,c).start();

  new Player(Pentium4,003,c).start();


  這個類暫時沒有什麼多說的,它是一個Main的角色。

  

  public class Player extends Thread{

  private final String name;

  private final String number;

  private final Corrie corrie;

  public Player(String name,String number,Corrie corrie) {

  this.name = name;

  this.number = number;

  this.corrie = corrie;

  public void run(){

  while(true){

  this.corrie.into(this.name,this.number);


  在這裏,我們把成員字段都設成final的,爲了說明一個Player一旦構造,他的名字和牌號就不能改變,簡單說在遊戲中,我,Sager,Pentium4三個人不會自己偷偷把自己的牌號換了,也不會偷偷地去鑽別的山洞,如果這個遊戲一旦發生錯誤,那麼錯誤不在我們玩家。

  

  import java.util.*;

  public class Corrie {

  private int count = 0;

  private String name;

  private String number;

  private HashMap lib = new HashMap(); //保存姓名與牌號的庫

  public Corrie(){

  lib.put(Axman,001);

  lib.put(Sager,002);

  lib.put(Pentium4,003);

  public void into(String name,String number){

  this.count ++;

  this.name = name;

  this.number = number;

  test():

  public String display(){

  return this.count+: + this.name + ( + this.number + );

  private void test(){

  elseSystem.out.println(ERR: + display());


  這個類中增加了一個lib的HashMap,相當於一個玩家姓名與牌號的庫,因爲明知道Corrie只有一個實例,所以我用了成員對象而不是靜態實例,只是爲了能在構造方法中初始化庫中的內容,從真正意義中說應該在一個輔助類中實現這樣的數據結構封裝的功能。如果不提供這個lib,那麼在check的時候就要用

  else if ……

  這樣複雜的語句,如果player大多可能會寫到手抽筋,所以用一個lib來chcek就非常容象。

  運行這個程序需要有一些耐心,因爲即使你的程序寫得再差在很多單線程測試環境下也能可是正確的。而且多線程程序在不同的機器上表現不同,要發現這個例子的錯識,可能要運行很長一段時間,如果你的機器是多CPU的,那麼出現錯誤的機會就大好多。

  在我的筆記本上最終出現錯誤是在11分鐘以後,出現的錯誤有幾鍾情況:

  1: ERR:Axman(003)

  2: ERR:Sager(002)

  第一種情況是檢查到了錯誤,我的牌號明明是001,卻打印出來003,而第二種明明沒有錯誤,卻打印了錯誤。

  另外還有可能會出現序號顛倒或不對應,但這個錯誤我們無法直觀地觀察,因爲你根本不知道哪個序號應該給哪個Player,而序號顛倒則有可能被滾動的屏幕所掩蓋。

  [正確的Critical Section模式的例子]

  我們知道出現這些錯誤是因爲Corrie類的方法不是線程安全的,那麼只要修改Corrie類爲線程安全的類就行了。其它類則不需要修改,上面說過,如果出現錯誤那一定不是我們玩家的事:

  

  import java.util.*;

  public class Corrie {private int count = 0;

  private String name;

  private String number;

  private HashMap lib = new HashMap(); //保存姓名與牌號的庫

  public Corrie(){lib.put(Axman,001);

  lib.put(Sager,002);

  lib.put(Pentium4,003);

  public synchronized void into(String name,String number){

  this.count ++;

  this.name = name;

  this.number = number;

  test();

  public synchronized String display(){

  return this.count+: + this.name + ( + this.number + );

  private void test(){

  elseSystem.out.println(ERR: + display());


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章