1. Single Thread Execution Pattern
1. 概念
该Pattern用来限制多个线程同时对share resource的访问只让一个线程访问,一般通过synchronzied或lock来完成。
2. demo
Gate类(ShareResource),多线程共同访问。
public class Gate {
private int counter = 0;
private String name = "Nobody";
private String address = "Nowhere";
public synchronized void pass(String name, String address) {
this.counter++;
this.name = name;
this.address = address;
check();
}
public synchronized String toString() {
return "No. " + counter + " name: " + name + ", address: " + address;
}
private void check() {
if (name.charAt(0) != address.charAt(0)) {
System.out.println("******BROKEN*******" + toString());
}
}
}
public class UserThread extends Thread {
private final Gate gate;
private final String myname;
private final String myaddress;
public UserThread(Gate gate, String myname, String myaddress) {
this.gate = gate;
this.myname = myname;
this.myaddress = myaddress;
}
public void run() {
System.out.println(this.myname + "Begin");
while (true) {
gate.pass(this.myname, myaddress);
}
}
}
public class Main {
public static void main(String[] args) {
Gate gate = new Gate();
new UserThread(gate, "Alice", "Alaska").start();
new UserThread(gate, "Bobby", "Brazil").start();
new UserThread(gate, "Chris", "Canada").start();
}
}
3. 注意事项
- 使用场景
当多个线程同时访问共享资源,并可能进行修改,而修改的结果会影响其他线程的运行结果时,就需要用该模式。
- 性能
一般来说,该模式会使程序性能降低的原因有两点。一是不管synchronzied或者lock,获取锁对象都需要耗费时间。二是当某线程获得锁对象进入临界区,其他的线程
都需要等待该线程释放锁对象,这个状况称之为冲突。当冲突发生时,线程等待的时间就会使整个程序的心跟那个下降。故应该尽可能的缩小临界区范围,以减少出现线程冲突
的机会, 可抑制性能的降低。
2. Immutable Pattern
1. 概念
在Immutable Pattern中,有着能够保证实例状态绝不会改变的类(immutable类)。因为访问这个实例时,可以省去使用共享互斥机制所会浪费的时间,故若能妥善运
用,将能提高程序的性能。String类,基本类型的封装类(Integer, Long等)就是一个immutable的类。
2. demo
Person类,final类型,不可变
public final class Person {
private final String name;
private final String address;
public Person(String name, String address) {
this.name = name;
this.address = address;
}
public String getName() {
return this.name;
}
public String getAddress() {
return this.address;
}
public String toString() {
return "[ Person: name =" + name + ", address = " + address + " ]";
}
}
public class PrintPersonThread extends Thread {
private Person person;
public PrintPersonThread(Person persion) {
this.person = person;
}
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName() + " prints "
+ person);
}
}
}
Main测试
public class Main {
public static void main(String[] args){
Person alice = new Person("Alice", "Alaska");
new PrintPersonThread(alice).start();
new PrintPersonThread(alice).start();
new PrintPersonThread(alice).start();
}
}
3. 注意事项
- 使用场景
1. 当实例产生后,状态不再变化
2. 实例需要共享,且访问很频繁
- 性能
因不用获取锁和减少了冲突,善于该模式能有效提升程序的性能。
3. Guarded Suppension Pattern
1. 概念
当线程执行某个操作时,发现条件并不满足,就要求要执行该操作的线程等待挂起。
2. demo
public class Request {
private String name;
public Request(String name) {
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "[ Request " + name + " ]";
}
}
/**
*
* <pre>
* 项目名: ThreadPattern
* 类名: RequestQueue.java
* 类描述: 消息存储类
* </pre>
*/
public class RequestQueue {
final private LinkedList<Request> queue = new LinkedList<Request>();
public synchronized void putRequest(Request request) {//生产者生产消息后通知消费者消费
this.queue.addLast(request);
notifyAll();
}
public synchronized Request getRequest() {
while (this.queue.size() <= 0) {//如果执行条件不满足,则线程wait
try {
wait();
}catch (InterruptedException e) {
}
}
return queue.removeFirst();
}
}
/**
*
* <pre>
* 项目名: ThreadPattern
* 类名: ServerThread.java
* 类描述:生产者线程,生成消息并存储到消息存储队列中
* </pre>
*/
public class ServerThread extends Thread {
private Random random;
private RequestQueue queue;
public ServerThread(RequestQueue queue, String name, long seed) {
super(name);
this.queue = queue;
random = new Random(seed);
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
Request request = queue.getRequest();
System.out.println(Thread.currentThread().getName() + " handles " + request);
try {
Thread.sleep(random.nextInt(1000));
} catch (InterruptedException e) {
}
}
}
}
/**
*
* <pre>
* 项目名: ThreadPattern
* 类名: ClientThread.java
* 类描述: 消费者线程,从消息存储队列中消费消息,若无消息消费,则等待。
* </pre>
*/
public class ClientThread extends Thread {
private Random random;
private RequestQueue requestQueue;
public ClientThread(RequestQueue requestQueue, String name, long seed) {
super(name);
this.requestQueue = requestQueue;
this.random = new Random(seed);
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
Request request = new Request("No." + i);
System.out.println(Thread.currentThread().getName() + " requests " + request);
this.requestQueue.putRequest(request);
try {
Thread.sleep(this.random.nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Main {
public static void main(String[] args) {
RequestQueue queue = new RequestQueue();
ServerThread serverThread = new ServerThread(queue, "ServerThread", 21563L);
ClientThread clientThread = new ClientThread(queue, "ClientThread", 24871L);
serverThread.start();
clientThread.start();
}
}
3. 注意事项
- 使用场景
生产者消费者模式,异步执行的一种实现。
4. Balking Pattern
1. 概念
Balking Pattern与Guarded Suppension Pattern类似,区别在于当线程执行某一操作发现条件不满足时,是立即返回,中断执行,而不是等待条件满足。
2. demo
public class Data {
private final String filename;
private String content;
private boolean changed;
public Data(String filename, String content) {
this.filename = filename;
this.content = content;
this.changed = true;
}
/**
* 修改文件内容
* @param content
*/
public synchronized void change(String content) {
this.content = content;
this.changed = true;
}
/**
* 保存修改后的数据,保存到硬盘上
*/
public synchronized void save() {
while (!this.changed) {//如果发现文件没有被修改,则不做保存动作
return;
}
doSave();
this.changed = false;
}
private void doSave() {
System.out.println(Thread.currentThread().getName()
+ "calls doSave, content = " + this.content);
File file = new File(filename);
FileWriter writer = null;
try {
writer = new FileWriter(file, true);
writer.write(this.content);
} catch (IOException e) {
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
/**
*
* <pre>
* 项目名: ThreadPattern
* 类名: ChangerThread.java
* 类描述: 改变文件内容线程
* </pre>
*/
public class ChangerThread extends Thread {
private Data data;
private Random random = new Random();
public ChangerThread(String name, Data data) {
super(name);
this.data = data;
}
@Override
public void run() {
int i = 0;
while (true) {
i++;
String content = "No." + i;
this.data.change(content);
try {
Thread.sleep(random.nextInt(1000));
} catch (InterruptedException e) {
}
this.data.save();
}
}
}
/**
* <pre>
* 项目名: ThreadPattern
* 类名: SaverThread.java
* 类描述: 保存文件线程
* </pre>
*/
public class SaverThread extends Thread {
private Data data;
private Random random = new Random();
public SaverThread(String name, Data data) {
super(name);
this.data = data;
}
@Override
public void run() {
while (true) {
this.data.save();
try {
Thread.sleep(this.random.nextInt(1000));
} catch (InterruptedException e) {
}
}
}
}
/**
* <pre>
* 项目名: ThreadPattern
* 类名: Main.java
* 类描述: 测试
* </pre>
*/
public class Main {
public static void main(String[] args) {
Data data = new Data("data.txt", "(empty)");
new SaverThread("SaverThread", data).start();
new ChangerThread("ChangerThread", data).start();
}
}
3. 注意事项
- 使用场景
1. 不需要可以去执行的时候,如在上面的demo中,如果文件内容没有被修改,则不需要执行保存动作, 可提高程序性能。
2. 不想等待条件满足时,可提高程序的响应时间。
3. 某操作只需要执行一次时,如初始化。
5. Producer - Customer Pattern
1. 概念
Producer - Customer Pattern(生产者消费者模式),生产者生产消息,消费者消费消息,二者处理消息的速度可能不一致,那么需要一个缓存区来存放消息。
2. demo
/**
*
* <pre>
* 项目名: ThreadPattern
* 类名: Data.java
* 类描述:消息
* </pre>
*/
public class Data {
private String name;
public Data(String name) {
this.name = name;
}
@Override
public String toString() {
return "[ Data name = " + this.name + " ]";
}
}
/**
*
* <pre>
* 项目名: ThreadPattern
* 类名: Channel.java
* 类描述: 消息缓冲区,默认大小100,可以通过构造函数定制channel的大小。channel为FIFO模型
* </pre>
*/
public class Channel {
private final LinkedList<Data> buffer = new LinkedList<Data>();
private int bufferSize = 100;
public Channel() {
super();
}
public Channel(int channelSize) {
this.bufferSize = channelSize;
}
/**
* put数据到channel中,当channel的buffer大小大于或等于指定大小时,方法将进行等待
*
* @param data
*/
public synchronized void put(Data data) {
while (buffer.size() >= this.bufferSize) {
try {
wait();
} catch (InterruptedException e) {
}
}
this.buffer.addLast(data);
System.out.println(Thread.currentThread().getName() + " put data "+ data);
notifyAll();
}
/**
* 从channel中获取数据,当channel中没有数据时,进行等待
*
* @return
*/
public synchronized Data take() {
while (this.buffer.size() == 0) {
try {
wait();
} catch (InterruptedException e) {
}
}
Data data = this.buffer.removeFirst();
System.out.println(Thread.currentThread().getName() + " take date " + data);
notifyAll();
return data;
}
}
/**
*
* <pre>
* 项目名: ThreadPattern
* 类名: ProducerThread.java
* 类描述: 生产者线程,生成消息并放入到消息缓冲区中
* </pre>
*/
public class ProducerThread extends Thread {
private Channel channel;
private Random random = new Random();
private static int dataNo = 0;
public ProducerThread(String name, Channel channel) {
super(name);
this.channel = channel;
}
@Override
public void run() {
while (true) {
Data data = new Data("No." + nextDataNo());
this.channel.put(data);
try {
Thread.sleep(random.nextInt(1000));
} catch (InterruptedException e) {
}
}
}
public static synchronized int nextDataNo() {
return ++dataNo;
}
}
/**
*
* <pre>
* 项目名: ThreadPattern
* 类名: ComsumerThread.java
* 类描述:消费者线程,从消息缓存区Channel消费消息
* </pre>
*/
public class ComsumerThread extends Thread {
private Channel channel;
private Random random = new Random();
public ComsumerThread(String name, Channel channel) {
super(name);
this.channel = channel;
}
@Override
public void run() {
while (true) {
this.channel.take();
try {
Thread.sleep(random.nextInt(1000));
} catch (InterruptedException e) {
}
}
}
}
public class MainThread {
public static void main(String[] args) {
int channelSize = 1000;
Channel channel = new Channel(channelSize);
ProducerThread producer1 = new ProducerThread("Producer1", channel);
ProducerThread producer2 = new ProducerThread("Producer2", channel);
ComsumerThread comsumer1 = new ComsumerThread("Comsumer1", channel);
ComsumerThread comsumer2 = new ComsumerThread("Comsumer2", channel);
ComsumerThread comsumer3 = new ComsumerThread("Comsumer3", channel);
producer1.start();
producer2.start();
comsumer1.start();
comsumer2.start();
comsumer3.start();
}
}
3. 注意事项
- 使用场景
经常用作消息中心,异步消费消息,
6. Read - Write Lock Pattern
1. 概念
Reade-Write Lock Pattern将读取与写入分开来处理。在读取数据之前,必须获取用来读取的锁定。而要写入的时候,则必须获取用来写入的锁定。因为在读取的时候,实
例的状态不会改变,所以,就算有多个线程在同时读取也没有关系。但是,有线程在读取的时候,不能做写入的操作。写入的时候,实例的状态会该别,于是,在有一个线程写
入的时候,其他线程就不可以进行读取或者写入。
一般来说,进行共享互斥会使程序性能变差,但将写入的共享互斥与读取的共享分开来思考,就可以提升程序的性能。
2. demo
/**
*
* <pre>
* 项目名: ThreadPattern
* 类名: ReadWriteLock.java
* 类描述:读写锁
* </pre>
*/
public class ReadWriteLock {
/**
* 正在读取的线程数
*/
private int readingThreadsNumber;
/**
* 正在写入的线程数(最多为1)
*/
private int writingThreadsNumber;
/**
* 等待写入的线程数
*/
private int waitingWriteThreadsNumber;
/**
* 是否优先写入,true:优先写入;false:优先读取
*/
private boolean preferWriter = true;
public synchronized void readLock() throws InterruptedException {
// 如果有线程正在写入或者优先写入时,有线程正在等待写入,读取线程则等待
while (this.writingThreadsNumber > 0
|| (this.preferWriter && this.waitingWriteThreadsNumber > 0)) {
wait();
}
this.readingThreadsNumber++;
}
public synchronized void readUnlock() throws InterruptedException {
this.readingThreadsNumber--;
this.preferWriter = true;
notifyAll();
}
public synchronized void writeLock() throws InterruptedException {
this.waitingWriteThreadsNumber++;
// 如果有线程正在写入或者正在读取,当前写入线程等待
try {
while (this.writingThreadsNumber > 0
|| this.readingThreadsNumber > 0) {
wait();
}
} finally {
this.waitingWriteThreadsNumber--;
}
this.writingThreadsNumber++;
}
public synchronized void writeUnlock() throws InterruptedException {
this.writingThreadsNumber--;
this.preferWriter = false;
notifyAll();
}
}
/**
*
* <pre>
* 项目名: ThreadPattern
* 类名: Data.java
* 类描述:数据存储对象
* </pre>
*/
public class Data {
private char[] buffer;
private ReadWriteLock readWriteLock = new ReadWriteLock();
public Data(int size) {
this.buffer = new char[size];
for (int i = 0; i < size; i++) {
this.buffer[i] = '*';
}
}
public char[] read() throws InterruptedException {
try {
readWriteLock.readLock();
return doRead();
} finally {
readWriteLock.readUnlock();
}
}
public void write(char c) throws InterruptedException {
try {
readWriteLock.writeLock();
doWrite(c);
} finally {
readWriteLock.writeUnlock();
}
}
private char[] doRead() {
char[] newChars = new char[buffer.length];
System.arraycopy(this.buffer, 0, newChars, 0, this.buffer.length);
slowly();
return newChars;
}
private void doWrite(char c) {
for (int i = 0; i < this.buffer.length; i++) {
this.buffer[i] = c;
slowly();
}
}
private void slowly() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
}
/**
*
* <pre>
* 项目名: ThreadPattern
* 类名: ReaderThread.java
* 类描述:读线程
* </pre>
*/
public class ReaderThread extends Thread {
private static final Random random = new Random();
private final Data data;
public ReaderThread(Data data) {
this.data = data;
}
@Override
public void run() {
while (true) {
try {
char[] c = data.read();
System.out.println(Thread.currentThread().getName() + "reads " + String.valueOf(c));
Thread.sleep(random.nextInt(1000));
} catch (InterruptedException e) {
}
}
}
}
/**
*
* <pre>
* 项目名: ThreadPattern
* 类名: WriterThread.java
* 类描述:写线程
* </pre>
*/
public class WriterThread extends Thread {
private static final Random random = new Random();
private final Data data;
private final String filler;
private int index = 0;
public WriterThread(Data data, String filler) {
this.data = data;
this.filler = filler;
}
@Override
public void run() {
while (true) {
char c = nextChar();
try {
data.write(c);
Thread.sleep(random.nextInt(1000));
} catch (InterruptedException e) {
}
}
}
private char nextChar() {
char c = filler.charAt(index);
index++;
if (index > filler.length()) {
index = 0;
}
return c;
}
}
public class MainThread {
public static void main(String[] args) {
int bufferSize = 10;
Data data = new Data(bufferSize);
new ReaderThread(data).start();
new ReaderThread(data).start();
new ReaderThread(data).start();
new ReaderThread(data).start();
new ReaderThread(data).start();
new ReaderThread(data).start();
new ReaderThread(data).start();
String filler1 = "abcdefghjklmnopqrstuvwxyz";
String filler2 = "ABCDEFGHJKLMNOPQRSTUVWXYZ";
new WriterThread(data, filler1).start();
new WriterThread(data, filler2).start();
}
}
3. 注意事项
- 使用场景
1. 利用同时读不会冲突的特性,提高程序的性能
2. 适合读取操作十分频繁
3. 适合读取比写入次数频繁
7. Thread-Per-Message Pattern
1. 概念
Thread-Per-Message Pattern指每个消息一个线程,即对于每个命令或请求,分配一个线程去执行。
2. demo
/**
*
* <pre>
* 项目名: ThreadPattern
* 类名: Helper.java
* 类描述: 消息执行线程
* </pre>
*/
public class Helper {
public void handle(int count, char c) {
System.out.println("handle(" + count + ", " + c + ") BEGIN");
for (int i = 0; i < count; i++) {
System.out.print(c);
slowly();
}
System.out.println("");
System.out.println("handle( " + count + ", " + c + ") END");
}
private void slowly() {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
}
}
/**
*
* <pre>
* 项目名: ThreadPattern
* 类名: Host.java
* 类描述: 消息处理线程(委托给另一个线程去处理)
* </pre>
*/
public class Host {
private final Helper helper = new Helper();
public void request(final int count, final char c) {
System.out.println("reqeust (" + count + ", " + c + ") BEGIN");
new Thread() {//将消息委托给另外一个线程去处理
@Override
public void run() {
helper.handle(count, c);
}
}.start();
System.out.println("reqeust (" + count + ", " + c + ") END");
}
}
public class MainThread {
public static void main(String[] args) {
System.out.println("main Begin");
Host host = new Host();
host.request(10, 'a');
host.request(20, 'b');
host.request(30, 'c');
System.out.println("main End");
}
}
3. 注意事项
- 使用场景
1. 提高相应性, 降低延迟时间
2. 适合在操作顺序无所谓时使用
3. 不需要返回之的时候
4. 应用在服务器的制作,将服务端接受消息的线程和处理消息的线程分开
8. Worker Thread Pattern
1. 概念
Worker Threade Pattern指工作线程会依次抓意见工作来处理,当没有工作可作时,工作线程会停下来等待新的工作过来。Worker Thread 也被称为是Backgound
Thread(背景线程), 另外,也有人把管理工作线程的地方,称为 Thread Pool (线程池)。
2. demo
/**
*
* <pre>
* 项目名: ThreadPattern
* 类名: Request.java
* 类描述: 消息对象
* </pre>
*/
public class Request {
private final String name;
private final int number;
private final static Random random = new Random();
public Request(String name, int number) {
this.name = name;
this.number = number;
}
public void request() {
System.out.println(Thread.currentThread().getName() + " " + toString());
try {
Thread.sleep(random.nextInt(1000));
} catch (InterruptedException e) {
}
}
@Override
public String toString() {
return "[ Reqeust name = " + name + ", number = " + number + " ]";
}
}
/**
*
* <pre>
* 项目名: ThreadPattern
* 类名: Channel.java
* 类描述: 消息存储容器
* </pre>
*/
public class Channel {
private final LinkedList<Request> buffers = new LinkedList<Request>();
private static final int bufferSize = 100;
public synchronized void put(Request request) throws InterruptedException {
while (this.buffers.size() >= bufferSize) {
wait();
}
this.buffers.addLast(request);
notifyAll();
}
public synchronized Request take() throws InterruptedException {
while (this.buffers.size() == 0) {
wait();
}
Request request = this.buffers.removeFirst();
notifyAll();
return request;
}
}
/**
*
* <pre>
* 项目名: ThreadPattern
* 类名: ProducerThread.java
* 类描述: 生产者线程,将生产消息并将消息放到消息容器中
* </pre>
*/
public class ProducerThread extends Thread {
private final Channel channel;
private final static Random random = new Random();
public ProducerThread(String name, Channel channel) {
super(name);
this.channel = channel;
}
@Override
public void run() {
int i = 0;
while (true) {
Request request = new Request(getName(), ++i);
try {
this.channel.put(request);
Thread.sleep(random.nextInt(1000));
} catch (InterruptedException e) {
}
}
}
}
/**
*
* <pre>
* 项目名: ThreadPattern
* 类名: WorkerThread.java
* 类描述:工作线程,从消息存储器中拿到消息处理, 没有则等待
* </pre>
*/
public class WorkerThread extends Thread {
private Channel channel;
public WorkerThread(String name, Channel channel) {
super(name);
this.channel = channel;
}
@Override
public void run() {
while (true) {
try {
Request request = this.channel.take();
request.request();
} catch (InterruptedException e) {
}
}
}
}
/**
* <pre>
* 项目名: ThreadPattern
* 类名: WorkerTheradPool.java
* 类描述: 工作线程池
* </pre>
*/
public class WorkerTheradPool {
private WorkerThread[] threadPool;
public WorkerTheradPool(int threads, Channel channel) {
this.threadPool = new WorkerThread[threads];//定义一个工作线程组(线程池)
for (int i = 0; i < threads; i++) {
threadPool[i] = new WorkerThread("WorkerThread-" + (i + 1), channel);
}
}
public void startWorkers() {
for (int i = 0; i < this.threadPool.length; i++) {
threadPool[i].start();
}
}
}
public class Main {
public static void main(String[] args) {
int threads = 5;
Channel channel = new Channel();
WorkerTheradPool pool = new WorkerTheradPool(threads, channel);
new ProducerThread("Alice", channel).start();
new ProducerThread("Bobby", channel).start();
new ProducerThread("Chris", channel).start();
pool.startWorkers();
}
}
3. 注意事项
- 使用场景
1. 启动线程是个很繁杂的工作,可以先把线程创建好,用的时候再直接拿来用。
2. 控制承载量(即可以提供服务的线程数量),当任务繁重是可以扩大工作者线程数量,反之,可以减少。
9. Future Pattern
1. 概念
Futuren Pattern是指获取Futuren的线程,会在事后再去获取执行的结果。就好像拿提货单去领取蛋糕一样。如果已经有执行结果了, 就可以马上拿到数据;如果执行结果
还没有好,则继续等待执行结果出现为止。
2. demo
public interface Data {
public String getContent();
}
public class FutureData implements Data {
private RealData realData;
private boolean ready = false;
public synchronized void setRealData(RealData realData) {
if (ready) {
return;
}
this.realData = realData;
this.ready = true;
notifyAll();
}
@Override
public synchronized String getContent() {
while (!ready) {//如果结果还没处理好,则等待
try {
wait();
} catch (InterruptedException e) {
}
}
return this.realData.getContent();
}
}
public class RealData implements Data {
private String content;
public RealData(int count, char c) {
System.out.println("making RealData(" + count + ", " + c + ") Begin.");
//处理获取结果
char[] buffer = new char[count];
for (int i = 0; i < count; i++) {
buffer[i] = c;
slowly();
}
this.content = String.valueOf(buffer);
System.out.println("making RealData(" + count + ", " + c + ") End.");
}
@Override
public String getContent() {
return this.content;
}
private void slowly() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
}
public class Host {
public Data handle(final int count, final char c) {
System.out.println("handle ( " + count + ", " + c + ") Begin.");
final FutureData futureData = new FutureData();//创建Future对象
new Thread() {//将处理消息获取消息的工作交个另外一个线程去处理,参考Thread Per Message Pattern
@Override
public void run() {
RealData realData = new RealData(count, c);
futureData.setRealData(realData);
}
}.start();
System.out.println("handle ( " + count + ", " + c + ") End.");
return futureData;
}
}
public class Main {
public static void main(String[] args) {
System.out.println("main Begin.");
Host host = new Host();
Data data1 = host.handle(10, 'a');
Data data2 = host.handle(20, 'b');
Data data3 = host.handle(30, 'c');
System.out.println("main other job Begin.");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
}
System.out.println("main other job End.");
System.out.println("data1 = " + data1.getContent());
System.out.println("data2 = " + data2.getContent());
System.out.println("data3 = " + data3.getContent());
System.out.println("main End.");
}
}
3. 注意事项
- 使用场景
将任务交由另外一个线程去异步处理,自己能继续做自己的工作,即响应性不会降低,并且在工作线程处理完之后,还能获取处理结果。
10. Two-Phase Termination Pattern
1. 概念
我们将线程进行平常的处理的状态称为【作业中】, 当希望结束这个线程时,则送出【终止请求】。 接着这个线程,并不会马上结束, 而会开始处理必要的刷新工作。这
个状态称为【终止处理中】。 从【作业中】改变为【终止处理中】是第一个阶段。【终止处理中】的状态时,不会进行平常的操作,虽然线程还在运行,但是进行的是终止处
理。知道终止处理结束后,才真正结束线程。【终止处理中】的操作结束,是第二阶段。
可用来优雅的终止线程,一是安全的终止(安全性), 二是一定会进行终止处理(生命性), 三是在送出终止请求后,尽快的处理(响应性)。
2. demo
public class CountupThread extends Thread {
private boolean isShutdown = false;
private int count = 0;
@Override
public void run() {
try {
while (!isShutdown) {
doWork();
}
} catch (InterruptedException e) {
} finally {
doShutdown();
}
}
public void shutdownReqeust() {
this.isShutdown = true;//将shutDown标识设为true
interrupt();//仅置shutdown标识不正确,因为此时线程可能正在sleep或者wait,这样虽然sleep结束后,再来停止线程,但这样响应性就差饿了点。
}
private void doShutdown() {
System.out.println("doShutdown: current count is " + this.count);
}
private void doWork() throws InterruptedException {
System.out.println("curren count is " + ++count);
Thread.sleep(500);
}
}
public class MainThread {
public static void main(String[] args) {
System.out.println("main Begin.");
CountupThread countupThread = new CountupThread();
countupThread.start();
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
}
System.out.println("main : shutdown request.");
countupThread.shutdownReqeust();
System.out.println("main : join");
// 等待线程结束
try {
countupThread.join();
} catch (InterruptedException e) {
}
System.out.println("main End.");
}
}
3. 注意事项
- 使用场景
1. 不可以使用Thread类的stop方法,stop会强制停止线程,就肯能丧失安全性,因为说不定线程刚好在做临界区。
2. 进行繁重的处理前,先检查终止请求,这样一来,可使得程序的响应性提高。
11. Thread-Specific Storage Pattern
1. 概念
Thread-Specific Storage Pattern就是线程独有的存储库,针对每个线程提供的内存空间,独立于线程之外。ThreadLocal的实例可以想象成一种集合框架,该类的实例只
有一个,实现是用Map<Key, Value>, Key为当前线程的id,Value为设置的值。
2. demo
public class Log {
//存储各个线程的Log日志,ThreadLocal<T>, 实现是一个map, key为当前线程id,value为T
private static final ThreadLocal<TSLog> tsLogCollection = new ThreadLocal<TSLog>();
public static void println(String s) {
getTSLog().printWrite(s);
}
public static void close() {
getTSLog().close();
}
private static TSLog getTSLog() {
TSLog tsLog = tsLogCollection.get();
// 如果线程时第一次调用,新建立新文件并注册log
if (tsLog == null) {
tsLog = new TSLog(Thread.currentThread().getName() + "-log.txt");
tsLogCollection.set(tsLog);
}
return tsLog;
}
}
public class TSLog {
private PrintWriter writer;
public TSLog(String filename) {
try {
this.writer = new PrintWriter(filename);
} catch (FileNotFoundException e) {
}
}
public void printWrite(String s) {
writer.println(s);
}
public void close() {
writer.println("===========End of log===========");
writer.close();
}
}
public class ClientThread extends Thread {
public ClientThread(String name) {
super(name);
}
@Override
public void run() {
System.out.println(getName() + " Begin.");
for (int i = 0; i < 10; i++) {
Log.println("i = " + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
Log.close();
System.out.println(getName() + " End.");
}
}
public class MainThread {
public static void main(String[] args) {
new ClientThread("Alice").start();
new ClientThread("Bobby").start();
new ClientThread("Chris").start();
}
}
3. 注意事项
- 使用场景
1. 存放线程特有信息的地方
2. 线程安全的另种实现