多線程設計模式

Java多線程設計模式,幫助多線程功能提升質量,下降學習成本。主要的Pattern以下:
1.Singl e Threaded Execution Pattern 多個線程共享一個實例,這樣的話,多個線程都
   擅自改動實例的狀態,實例會喪失安全性。這種狀況能夠經過Java的關鍵詞synchronized來解決。如多我的
   經過一個gate時,只能一個個經過,那麼能夠以下的方式:
   public synchronized void pass(String name){
       this.name = name;
   }
   synchronized方法的性能比普通的方法低,因此下降減小使用。
   JDK中不少方法是synchronized,能夠安全使用,不少爲了性能是沒有同步。爲了提升性能能夠考慮使用  Immutable Pattern
2.Immutable Pattern 多個線程共享一個實例,可是實例的狀態不會改變,能夠提供throughput,但必須保證
   不變形(實例的狀態不會改變)。須要使用private,final等來支持。
3.Guarded Suspension Pattern 多個線程共享一個實例,這樣的話,多個線程都
   擅自改動實例的狀態,實例會喪失安全性。當實例的狀態不恰當時,就要求線程等待到合適的狀態,以「警惕條     件」來表示實例的「適當的狀態」。若是警惕條件一直不成立,線程會永遠等待下去,會使程序喪失生命性。Java     中用while循環來測試警惕條件,使用wait方法讓線程等待,並使用notify/notifyAll通知警惕條件的改變。
檢     驗、修改警惕條件是,會使用Single Threaded Execution Pattern。Pattern的例子以下:
   public class RequestQueue{

           private final LinkedList queue = new LinkedList();
           public synchronized Request getRequest(){
       while(queue.size() <= 0){     //警惕條件
                try{      
              wait();
                  }catch(InterruptedException e){}
       }
       return (Request)queue.removeFirst();
           }
           public synchronized void putRequest(Request request){
              queue.addLast(request);
              notifyAll();

           }

   }


   以上使用Queue的客戶端和服務器代碼裏面很是乾淨,沒有多線程的東西,代碼複用性很好。
   當警惕條件不成立時想要立刻退出,就使用Balking Pattern
4.Balking Pattern 一直等待安全的時機,會使程序的響應性下降。Java語言中,檢驗警惕條件時要使用if語句
   ,當要balk時,可以使用return退出方法,或者throw拋出異常。
   public class Data {
       private String filename;       //修改是的名字
       private String content;         // 資料的內容
       private boolean changed;       //修改後的內容還沒存儲的話,值爲true

       public Data(String filename, String content) {
               this.filename = filename;
               this.content = content;
               this.changed = true;
       }

       // 修改資料內容
       public synchronized void change(String newContent) {              
               content = newContent;                                                                    
               changed = true;                                                                                    
       }                                                                                                                    

       // 如有資料修改,就存儲到擋安裏
       public synchronized void save() throws IOException {          
               if (!changed) {                                                                                    
                       System.out.println(Thread.currentThread().getName() + " balks");
                       return; //沒有就退出                                                                                        
               }                                                                                                            
               doSave();                                                                                        
               changed = false;                                                                                  
       }                                                                                                                    

       // 實際資料儲存到擋案裏用的方法
       private void doSave() throws IOException {
               System.out.println(Thread.currentThread().getName() + " calls doSave, content = " + content);
               Writer writer = new FileWriter(filename);
               writer.write(content);
               writer.close();
       }
}

5.Producer-Consumer Pattern 當Producer參與者與Consumer參與者處理的速度不一樣時,速度慢的會扯速度快的
   後腿,而下降程序的throughput。解決的辦法就是在二者之間,加上中繼用的Channel參與者。並讓Channel
   參與者存放多條數據,這樣就能夠緩衝Producer和Consumer之間處理速度的差別。這個模式使用了Guarded
   Suspension Pattern。

   public class Table {
       private final String[] buffer;
       private int tail;   /下一個放put的地方
       private int head;   //下一個放的take地方
       private int count; // buffer內的蛋糕數
       public Table(int count) {
               this.buffer = new String[count];
               this.head = 0;
               this.tail = 0;
               this.count = 0;
       }
       // 放置蛋糕
       public synchronized void put(String cake) throws InterruptedException {
               System.out.println(Thread.currentThread().getName() + " puts " + cake);
               while (count >= buffer.length) {
                       wait();
               }
               buffer[tail] = cake;
               tail = (tail + 1) % buffer.length;
               count++;
               notifyAll();
       }
       // 取得蛋糕
       public synchronized String take() throws InterruptedException {
               while (count <= 0) {
                       wait();
               }
               String cake = buffer[head];
               head = (head + 1) % buffer.length;
               count--;
               notifyAll();
               System.out.println(Thread.currentThread().getName() + " takes " + cake);
               return cake;
       }
   }

6.Read-Write Lock Pattern 多個線程共享一個實例,如進程之間不進行共享胡扯,會喪失安全性。
   但使用Single Threaded Execution Pattern會使程序throughput下降。解決的方法就是將控制reader參與者的       鎖定與控制writer參與者的鎖定分開,加入ReadWriteLock參與者,以提供兩種不一樣的鎖定。
 
  public final class ReadWriteLock {
       private int readingReaders = 0; // (A)...實際正在讀取的執行緒數量
       private int waitingWriters = 0; // (B)...正在等待寫入的執行緒數量
       private int writingWriters = 0; // (C)...實際正在寫入的執行緒數量
       private boolean preferWriter = true; // 寫入優先的話,值爲true

       public synchronized void readLock() throws InterruptedException {
               while (writingWriters > 0 || (preferWriter && waitingWriters > 0)) {
                       wait();
               }
               readingReaders++;                                             //   (A)實際正在讀取的線程數量加1
       }

       public synchronized void readUnlock() {
               readingReaders--;                                             //   (A)實際正在讀取的線程數量減1
               preferWriter = true;
               notifyAll();
       }

       public synchronized void writeLock() throws InterruptedException {
               waitingWriters++;                                             // (B)正在等待寫入的線程數量加1
               try {
                       while (readingReaders > 0 || writingWriters > 0) {
                               wait();
                       }
               } finally {1
                   waitingWriters--;                                     // (B)正在等待寫入的線程數量減1
               }
               writingWriters++;                                             //   (C)實際正在寫入的線程數量加1
       }

       public synchronized void writeUnlock() {
               writingWriters--;                                             // (C)實際正在寫入的線程數量減
               preferWriter = false;
               notifyAll();
       }
   }
 
   public class Data {
       private final char[] buffer;
       private final ReadWriteLock lock = new ReadWriteLock();
       public Data(int size) {
               this.buffer = new char[size];
               for (int i = 0; i < buffer.length; i++) {
                       buffer[i] = '*';
               }
       }
       public char[] read() throws InterruptedException {
               lock.readLock();
               try {
                       return doRead();
               } finally {
                       lock.readUnlock();
               }
       }
       public void write(char c) throws InterruptedException {
               lock.writeLock();
               try {
                       doWrite(c);
               } finally {
                       lock.writeUnlock();
               }
       }
       private char[] doRead() {
               char[] newbuf = new char[buffer.length];
               for (int i = 0; i < buffer.length; i++) {
                       newbuf[i] = buffer[i];
               }
               slowly();
               return newbuf;
       }
       private void doWrite(char c) {
               for (int i = 0; i < buffer.length; i++) {
                       buffer[i] = c;
                       slowly();
               }
       }
       private void slowly() {
               try {
                       Thread.sleep(50);
               } catch (InterruptedException e) {
               }
       }
   }

7.Thread-Per-Message Pattern   在方法的屬性處理完成以前,控制權不會從Host參與者退出。若是方法的處理
   屬性很話費時間,程序的響應性能會下降。解決的方式就在Host的參與者裏,啓動新的線程,而且將該方法應       該進行的工做交給這個心的線程,這樣Client參與者的線程能夠繼續執行下一個操做,這樣作,不用更改       Client參與者的程序代碼,並能提升程序的響應性。想節省啓動線程所花費的時間,可使用Worker Thread       Pattern。
   public class Host {
       private final Helper helper = new Helper();
       public void request(final int count, final char c) {
               System.out.println("       request(" + count + ", " + c + ") BEGIN");
               new Thread() {
                       public void run() {
                               helper.handle(count, c);
                       }
               }.start();
               System.out.println("       request(" + count + ", " + c + ") END");
       }
   }

8.Worker Thread Pattern 若是方法的處理屬性很花時間,程序的響應性會下降。爲了提供響應性,而啓動新
   的線程來處理方法時,啓動線程所花的時間又會下降throughput。另外當送出的請求太多時,會啓動
   過多的線程,這會使承載量變差。
   public class Channel {
       private static final int MAX_REQUEST = 100;
       private final Request[] requestQueue;
       private int tail;   // 下一個putRequest的地方
       private int head;   // 下一個takeRequest的地方
       private int count; // Request的數量

       private final WorkerThread[] threadPool;

       public Channel(int threads) {
               this.requestQueue = new Request[MAX_REQUEST];
               this.head = 0;
               this.tail = 0;
               this.count = 0;

               threadPool = new WorkerThread[threads];
               for (int i = 0; i < threadPool.length; i++) {
                       threadPool[i] = new WorkerThread("Worker-" + i, this);
               }
       }
       public void startWorkers() {
               for (int i = 0; i < threadPool.length; i++) {
                       threadPool[i].start();
               }
       }
       public synchronized void putRequest(Request request) {
               while (count >= requestQueue.length) {
                       try {
                               wait();
                       } catch (InterruptedException e) {
                       }
               }
               requestQueue[tail] = request;
               tail = (tail + 1) % requestQueue.length;
               count++;
               notifyAll();
       }
       public synchronized Request takeRequest() {
               while (count <= 0) {
                       try {
                               wait();
                       } catch (InterruptedException e) {
                       }
               }
               Request request = requestQueue[head];
               head = (head + 1) % requestQueue.length;
               count--;
               notifyAll();
               return request;
       }
   }

9.Future Pattern 當Client會將工做委託給其餘線程,而Client參與者但願獲得處理的結果。將工做委託給
   別人時,若是又等待執行結果,會使響應性下降。
   public class FutureData implements Data {
       private RealData realdata = null;
       private boolean ready = false;
       public synchronized void setRealData(RealData realdata) {
               if (ready) {                                              
                       return;         // balk
               }
               this.realdata = realdata;
               this.ready = true;
               notifyAll();
       }
       public synchronized String getContent() {
               while (!ready) {
                       try {
                               wait();
                       } catch (InterruptedException e) {
                       }
               }
               return realdata.getContent();
       }
   }

附多線程程序的評價標準
一、安全性——不損壞對象 對象損壞是指對象的狀態不符合設計師的原意,一般是獲取對象的狀態值並不是預期值。
二、生存性——進行必要的處理 也許不是如今,可是必定會進行必要的處理,若是程序安全了,可是有些必要的處理得不到操做,那麼這個多線程程序也是不合格的。
三、複用性——可再利用類 寫多線程程序,若是可以將多線程的共享和互斥結構隱藏在類裏面,這就是一個高度可複印的程序。
四、性能——能快速大量處理 主要表如今吞吐量(Throughput)即必定時間內能完成的處理量,能完成的處理量越多,表示數據吞吐量越大;容量(Capacity)指可同時處理 的數量;響應性(Responsiveness)指從發出請求到收到響應的時間,時間越短,響應性越高。
五、伸縮性(Scalability)等

前兩個是必要條件,後面幾個是程序質量的描述
相關文章
相關標籤/搜索