和風細雨

          世上本無難事,心以為難,斯乃真難。茍不存一難之見于心,則運用之術(shù)自出。

          讀寫鎖的使用

          本文內(nèi)容

          何時該使用讀寫鎖.
          讀寫鎖的寫法.
          理解讀寫鎖和線程互斥的區(qū)別。

          復(fù)習(xí)-同步化的概念

          當一個方法或代碼塊被聲明成synchronized,要執(zhí)行此代碼必須先取得一個對象實例或this的鎖定,這個鎖定要在synchronized修飾的方法或代碼塊執(zhí)行完后才能釋放掉(無論這段代碼是怎樣返回的,是正常運行還是異常運行)。每個對象只有一個鎖定,如果有兩個不同的線程試圖同時調(diào)用同一對象的同步方法,最終只會有一個能運行此方法,另外一個要等待第一個線程釋放掉鎖定后才能運行此方法。

          讀寫鎖應(yīng)用的場合

          我們有時會遇到對同一個內(nèi)存區(qū)域如數(shù)組或者鏈表進行多線程讀寫的情況,一般來說有以下幾種處理方式: 1.不加任何限制,多見于讀取寫入都很快的情況,但有時也會出現(xiàn)問題. 2.對讀寫函數(shù)都加以同步互斥,這下問題是沒了,但效率也下去了,比如說兩個讀取線程不是非要排隊進入不可. 3.使用讀寫鎖,安全和效率都得到了解決,特別合適讀線程多于寫線程的情況.也就是下面將要展現(xiàn)的模式.

          讀寫鎖的意圖

          讀寫鎖的本意是分別對讀寫狀態(tài)進行互斥區(qū)分,有互斥時才加鎖,否則放行.互斥的情況有: 1.讀寫互斥. 2.寫寫互斥. 不互斥的情況是:讀讀,這種情況不該加以限制. 程序就是要讓鎖對象知道當前讀寫狀態(tài),再根據(jù)情況對讀寫的線程進行鎖定和解鎖。

          讀寫線程都要操作的數(shù)據(jù)類

          讀寫線程都要操作的數(shù)據(jù)是鏈表datas。
          注意其中try...finally 的寫法,它保證了加鎖解鎖過程是成對調(diào)用的

           

          lpublic class DataLib {
               private List<String> datas;
           
               private ReadWriteLock lock;
           
               public DataLib() {
                   datas = new ArrayList<String>();
                   lock = new ReadWriteLock();
               }
           
               // 寫入數(shù)據(jù),這時不能讀取
               public void writeData(List<String> newDatas) {
                   try {
                       lock.writeLock();
                       Test.sleep(2);
                       datas=newDatas;
                   } finally {
                       lock.writeUnlock();
                   }
               }
           
               // 讀取數(shù)據(jù),這時不能寫入
               public List<String> readData() {
                   try {
                       lock.readLock();
                       Test.sleep(1);            
                       return datas;
                   } finally {
                       lock.readUnlock();
                   }
           
               }
           
           }



           

          讀寫鎖ReadWriteLock類

          public class ReadWriteLock{
              // 讀狀態(tài)
              private boolean isRead;
             
              // 寫狀態(tài)
              private boolean isWrite;
             
              public synchronized void readLock(){
                  // 有寫入時讀取線程停止
                  while(isWrite){
                      try{   
                          System.out.println("有線程在進行寫入,讀取線程停止,進入等待狀態(tài)");
                          wait();
                      }
                      catch(InterruptedException ex){
                          ex.printStackTrace();
                      }
                  }
                 
                  System.out.println("設(shè)定鎖為讀取狀態(tài)");
                  isRead=true;
              }
             
              public synchronized void readUnlock(){
                  System.out.println("解除讀取鎖");
                  isRead=false;
                  notifyAll();
              }

               public synchronized void writeLock(){
                  // 有讀取時讀取線程停止
                  while(isRead){
                      try{   
                          System.out.println("有線程在進行讀取,寫入線程停止,進入等待狀態(tài)");
                          wait();
                      }
                      catch(InterruptedException ex){
                          ex.printStackTrace();
                      }
                  }
                 
                  // 有寫入時寫入線程也一樣要停止
                  while(isWrite){
                      try{   
                          System.out.println("有線程在進行寫入,寫入線程停止,進入等待狀態(tài)");
                          wait();
                      }
                      catch(InterruptedException ex){
                          ex.printStackTrace();
                      }
                  }
                 
                  System.out.println("設(shè)定鎖為寫入狀態(tài)");
                  isWrite=true;
              }
             
              public synchronized void writeUnlock(){
                  System.out.println("解除寫入鎖");
                  isWrite=false;
                  notifyAll();
              }
          }

          寫線程類Writer -它用于往DataLib類實例中的datas字段寫數(shù)據(jù)

          分析其中dataLib字段的用意。
          注意并記住其中持續(xù)調(diào)用及使用隨機數(shù)的方法。

           

          lpublic class Writer implements Runnable{
               private DataLib dataLib;
               private static final Random random=new Random();
               private String[] mockDatas={"","","","","","","","","",""};    
               
               public Writer(DataLib dataLib,String[] mockDatas){
                   this.dataLib=dataLib;
                   this.mockDatas=mockDatas;
                   
                   Thread thread=new Thread(this);
                   thread.start();
               }
               
               public void run(){
                   while(true){
                       Test.sleep(random.nextInt(3));
                       
                       int startIndex=random.nextInt(mockDatas.length);
                       
                       ArrayList<String> newDatas=new ArrayList<String>();
                       for(int i=startIndex;i<mockDatas.length;i++){
                           newDatas.add(mockDatas[i]);
                       }
                       
                       dataLib.writeData(newDatas);
                   }
               }
           }

          讀線程類Reader  -它用于從DataLib類實例中的datas字段讀取數(shù)據(jù)

          分析其中dataLib字段的用意。
          注意并記住其中持續(xù)調(diào)用及使用隨機數(shù)的方法。

          public class Reader implements Runnable{
              private DataLib dataLib;
              private static final Random random=new Random();
             
              public Reader(DataLib dataLib){
                  this.dataLib=dataLib;
             
                  Thread thread=new Thread(this);
                  thread.start();
              }
             
              public void run(){
                  while(true){
                      Test.sleep(random.nextInt(2));           
                      List<String> datas=dataLib.readData();
                     
                      System.out.print(">>取得數(shù)組為:");
                      for(String data:datas){
                          System.out.print(data+",");
                      }
                      System.out.print("\n");
                  }
              }
          }

          將代碼運行起來

          右邊的代碼創(chuàng)建了兩個寫線程和三個讀線程,它們都是對dataLib實例進行操作的。
          五個線程都有一個dataLib字段,都提供了一個帶參構(gòu)造函數(shù)以給datas字段賦值,這就保證了五個線程操作的都是一個實例的同一字段,也就是同一片內(nèi)存。
          讀寫鎖就是對這五個線程進行控制的。
          當有一個讀線程在操作時,其它的寫線程無法進行操作,讀線程可以正常操作,互不干擾。
          當有一個寫線程在操作時,其它的讀線程無法進行操作。

           

           public class Test{
               public static void main(String[] args){
                   DataLib dataLib=new DataLib();
                   
                   String[] mockDatas1={"","","","","","","","","",""};
                   Writer writer1=new Writer(dataLib,mockDatas1);
                   
                   String[] mockDatas2={"","","","","","","","","","","",""};
                   Writer writer2=new Writer(dataLib,mockDatas2);
                   
                   Reader reader1=new Reader(dataLib);
                   Reader reader2=new Reader(dataLib);
                   Reader reader3=new Reader(dataLib);
               }
               
               
               // 用于延時
               public static void sleep(int sleepSecond){
                   try{
                       Thread.sleep(sleepSecond*1000);
                   }
                   catch(Exception ex){
                       ex.printStackTrace();
                   }
               }
           }

          小結(jié)

          當多個線程試圖對同一內(nèi)容進行讀寫操作時適合使用讀寫鎖。
          請理解并記住ReadWriteLock類讀寫鎖的寫法.
          讀寫鎖相對于線程互斥的優(yōu)勢在于高效,它不會對兩個讀線程進行盲目的互斥處理,當讀線程數(shù)量多于寫線程尤其如此,當全是寫線程時兩者等效。

          posted on 2008-02-22 14:28 和風細雨 閱讀(2615) 評論(2)  編輯  收藏 所屬分類: 線程

          評論

          # re: 讀寫鎖的使用 2010-11-26 17:04 ydu

          相同線程會死鎖
          read.lock()
          write.lock()
          write.unlock();
          read.unload();  回復(fù)  更多評論   

          # .[未登錄] 2014-11-12 15:08 .

          .  回復(fù)  更多評論   

          主站蜘蛛池模板: 湟中县| 潍坊市| 额尔古纳市| 双柏县| 桦南县| 肇东市| 凉山| 崇义县| 福海县| 崇礼县| 浙江省| 台东县| 麟游县| 岱山县| 广州市| 开原市| 汉川市| 柞水县| 盐山县| 政和县| 哈尔滨市| 扎囊县| 桐梓县| 平武县| 永川市| 清涧县| 南京市| 嘉荫县| 积石山| 东城区| 同江市| 阿拉尔市| 南昌县| 西和县| 伊宁市| 南丰县| 乌恰县| 茂名市| 育儿| 五莲县| 翁牛特旗|