Java多線程設計模式,幫助多線程功能提高質量,降低學習成本。主要的Pattern如下:
1.Single 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();
    }
  }


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

前兩個是必要條件,后面幾個是程序質量的描述