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)等

前两个是必要条件,后面几个是程序质量的描述