• 多线程设计模式读书笔记(一)


    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());
    		}
    	}
    }


    UserThread类
    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);
    		}
    	}
    }


    Main测试程序
    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 + " ]";
    	}
    }

    Thread类
    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.  线程安全的另种实现
  • 相关阅读:
    EF架构~简洁关联表插入,优越的代码性能!
    基础才是重中之重~你是否真正了解TransactionScope?
    基础才是重中之重~如何整理BLL与DAL层的文件
    java Byte 和byte 差别及byte[ ]和string转换
    转: java的InputStream和OutputStream的理解
    java.awt.list java.util.list 区别
    java.util.Scanner 总结
    java .io OutputStream 与InputStream
    java 3中方法复制一个文件
    网络爬虫 简介
  • 原文地址:https://www.cnblogs.com/marcotan/p/4256850.html
Copyright © 2020-2023  润新知