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. 线程安全的另种实现