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)等
前两个是必要条件,后面几个是程序质量的描述