I have to say that I seldom have the use of multithreaded programming techniques in my work. Because I often use a framework like SSH to build an MVC architecture, I am more accustomed to writing boilerplate code that some good programmers despise. Recently read the text of the Sea of multi-threaded programming practical guide, instantly bright. I think there are a lot of things that I can learn, in fact, I have used the two-phase termination mode, serial closed mode, producer consumer mode, and thread pool technology in the recent project, and it does improve the throughput of our server in many ways. Speaking here I spit groove, because it is after graduation career change, at present also only work a year still less than 2 months. So forgive me, but I believe I will do better in the future!
Personal feeling of that is a very strong practical book, very reading value. But I'm not here to play commercials. Talk less and get to the chase. What I'm going to talk about today is my understanding of the plumbing patterns in the book. The so-called Pipe line mode, my understanding is to divide a task into multiple stages, the completion of the task needs to go through all stages to complete. Then, if the multi-threaded pattern is used, the different phases of the task are done in different threads. The task submitted by the client or client code, then, is returned as soon as phase one processing is complete, although the task may still be in process. The advantage of this is to reduce the wait for the client code. But in fact, any specific task is still serially executed, which avoids some of the most tangled thread-safety issues. However, if the client code simultaneously commits multiple tasks concurrently, the thread that processes task one will be able to process the phase one of task two immediately after processing this specific phase, although the task may still be in phase two or phase three. So, in the case of multi-task submissions. The pipeline pattern also has the effect of concurrent execution, let's call it pseudo concurrency.
This blog is written because the pipeline pattern involves too many other patterns, this mode can involve the use of thread pool, can find the shadow of the responsibility chain pattern, can use the adorner mode, as well as multi-threaded programming in the two-phase termination mode and so on. So it is advantageous to consolidate oneself to learn, and increase oneself of comprehensive combat ability. Haha, do not say, we come to the specific look at the code bar.
First of all, this is the abstraction of the individual pipelines, because the task is processed sequentially in each pipeline, the task is bound to flow to the next pipeline after processing to a certain stage in a pipeline, so the interface defines the Setnextpipe () method to set the next pipe, etc. It also defines how the context information is passed in the initialization method, and so on, and the rest is no longer mentioned:
<span style= "FONT-SIZE:18PX;" >public interface Pipe<in, out>{/** * Sets the next pipe instance for the current pipe instance. * @param nextpipe * the next pipe instance */void setnextpipe (pipe<?,? > nextpipe);/** * Initializes the service provided externally by the current pipe instance. * @param pipectx */void init (pipecontext pipectx);/** * Stop the service provided externally by the current pipe instance. * * @param timeout * @param unit */void shutdown (long timeout, timeunit unit),/** * processing the INPUT element and processing the result as input to the next pipe instance. */void process (in input) throws interruptedexception;} </span>
Let's take a look at the abstraction of the context, and note that this interface is an abstraction of the computing environment for each processing phase, mainly for exception handling.
<span style= "FONT-SIZE:18PX;" >public Interface pipecontext{/** * is used to process exceptions thrown during the processing phase. * @param exp */void handleError (pipeexception exp);} </span>
from the pipeline context interface you can see that the exceptions that can be thrown in each pipeline are uniformly handled, so let's take a look at this custom pipe exception object (pipeexception):
<span style= "FONT-SIZE:18PX;" >public class Pipeexception extends exception{private static final long serialversionuid = -2944728968269016114l;/** * Throws an exception to the pipe instance. */public final pipe<?,? > sourcepipe;/** * Throws an exception to the INPUT element that the Pipe instance handles when throwing an exception. */public final Object input;public pipeexception (pipe<?,? > sourcepipe, Object Input, String message) {super ( message); this.sourcepipe = Sourcepipe;this.input = input;} Public pipeexception (pipe<?,? > sourcepipe, Object Input, String message, Throwable cause) {Super (message, cause); This.sourcepipe = Sourcepipe;this.input = input;}} </span>
OK, then let's look at an abstract implementation class for pipelines,
<span style= "font-size:18px;" >public abstract class Abstractpipe<in, out> implements Pipe<in, out>{protected volatile pipe<?,? > Nextpipe = null;protected volatile pipecontext pipectx; @Overridepublic void init (PipeContext pipectx) {this.pipectx = Pipectx;} @Overridepublic void Setnextpipe (pipe<?,? > Nextpipe) {this.nextpipe = Nextpipe;} @Overridepublic void shutdown (long timeout, timeunit unit) {//do nothing}/** * leave the subclass implementation. Used for subclasses to implement their task processing logic. * * @param INPUT Element (Task) * @return Task processing result * @throws pipeexception */protected abstract out doprocess Put) throws pipeexception; @SuppressWarnings ("Unchecked") public void process (in input) throws Interruptedexception{try {out-out = doprocess (input); if (null! = Nextpipe) {if (null! = out) {((Pipe<out,?>) nextpipe). Process (out);}} catch (Interruptedexception e) {thread.currentthread (). interrupt ();} catch (Pipeexception e) {pipectx.handleerror (e);}}} </span>
Since the task is to be processed sequentially in each pipeline, a pipe is given an intermediate result based on the input, and as input to the next pipe, until the final result is obtained, then how are these pipes concatenated together? The next interface is to define how to concatenate the pipe line interface of each pipeline, take a closer look and think about it, is not able to find the responsibility chain pattern of shadow it, haha.
<span style= "FONT-SIZE:18PX;" >public interface Pipeline<in, out> extends Pipe<in, out>{/** * Add a Pipe instance to the Pipeline instance. * * @param pipe * Pipe example */void addpipe (pipe<?,? > pipe);} </span>
The light has an interface or does not see what the name of Ah, and look at the specific implementation of the pipeline line, you can see that the pipeline object has a method of adding pipelines to add a different pipeline, and in the initialization process set up the pipeline of the next pipeline is who. In addition, the initialization task is done helperexecutor the thread pool object that is built into the threading object and submitted to the pipe line object. Add a method There are two overloaded methods are Addasthreadpoolbasedpipe and Addasworkerthreadbasedpipe, the two methods added to the pipe object is decorated, I did not say that the pipeline mode is also related to decorative mode, Decoration mode is here to reflect the oh, haha:
<span style= "FONT-SIZE:18PX;" >public class simplepipeline<t, out> extends Abstractpipe<t, out> implements Pipeline<t, out>{ Private final queue<pipe<?,? >> pipes = new linkedlist<pipe<?,? >> ();p rivate final Executorservice helperexecutor;public Simplepipeline () {This (Executors.newsinglethreadexecutor (new ThreadFactory () {@Overridepublic thread newthread (Runnable r) {Thread t = new Thread (R, "Simplepipeline-helper"); T.setdaemon (true); return t;}));} Public Simplepipeline (Final Executorservice helperexecutor) {super (); this.helperexecutor = Helperexecutor;} @Overridepublic void shutdown (long timeout, timeunit unit) {pipe<?,? > pipe;while (Null! = (Pipe = Pipes.poll ())) {PIP E.shutdown (timeout, unit);} Helperexecutor.shutdown ();} @Overrideprotected out doprocess (T input) throws pipeexception{//do nothing to return null;} @Overridepublic void Addpipe (pipe<?,? > Pipe) {//pipe the correlation relation between the pipes is established pipes.add (pipe) in the Init method;} Public <input, output> void AddasworkertHreadbasedpipe (Pipe<input, output> delegate, int workercount) {addpipe (new workerthreadpipedecorator<input, Output> (delegate, Workercount));} Public <input, output> void Addasthreadpoolbasedpipe (Pipe<input, output> delegate, Executorservice Executorserivce) {addpipe (new threadpoolpipedecorator<input, output> (delegate, Executorserivce));} @Overridepublic void process (T input) throws interruptedexception{@SuppressWarnings ("unchecked") pipe<t,?> Firstpipe = (pipe<t,?>) Pipes.peek (); firstpipe.process (input);} @Overridepublic void Init (final pipecontext ctx) {linkedlist<pipe<?,? >> pipeslist = (linkedlist<pipe <?,? >>) pipes; pipe<?,? > prevpipe = This;for (pipe<?,? > Pipe:pipeslist) {prevpipe.setnextpipe (pipe);p revpipe = pipe;} Runnable task = new Runnable () {@Overridepublic void Run () {for (pipe<?,? > Pipe:pipes) {pipe.init (CTX);}}; Helperexecutor.submit (Task);} Public PipeContext Newdefaultpipelinecontext () {return New PipeContext () {@Overridepublic void HandleError (Final pipeexception exp) {helperexecutor.submit (new Runnable () {@ overridepublic void Run () {exp.printstacktrace ();}});}};} </span>
The above code involves two of pipe line decoration class, one is Workerthreadpipedecorator, this decorator I do not post out, because the next demo will not be used, and this article a lot of code, if someone wants to actually run this demo, Comment out the overloaded method for adding pipelines to this decoration class. The other is threadpoolpipedecorator. It is clear that the task of the pipeline to delegate the thread pool execution, of course, this execution process for the client code is transparent, decoration is still a pipe oh, this is the intention of the decoration mode Oh, the specific look at the code:
<span style= "FONT-SIZE:18PX;" >public class Threadpoolpipedecorator<in, out> implements Pipe<in, Out>{private final pipe<in, out > delegate;private final executorservice executorserivce;//thread pool stop flag. Private final Terminationtoken terminationtoken;private final countdownlatch stageprocessdonelatch = new CountDownLatch (1);p ublic threadpoolpipedecorator (pipe<in, out> delegate, Executorservice executorserivce) {this.delegate = Delegate;this.executorserivce = Executorserivce;this.terminationtoken = Terminationtoken.newinstance ( EXECUTORSERIVCE);} @Overridepublic void init (PipeContext pipectx) {delegate.init (PIPECTX);} @Overridepublic void process (final in input) throws interruptedexception{runnable task = new Runnable () {@Overridepublic V OID run () {int remainingreservations = -1;try{delegate.process (input);} catch (Interruptedexception e) {;} Finally{remainingreservations = TerminationToken.reservations.decrementAndGet ();} if (Terminationtoken.istoshutdown () && 0 = = REmainingreservations) {Stageprocessdonelatch.countdown ();}}; Executorserivce.submit (Task); TerminationToken.reservations.incrementAndGet ();} @Overridepublic void shutdown (long timeout, timeunit unit) {terminationtoken.setistoshutdown (); TerminationToken.reservations.get () > 0) {try{if (Stageprocessdonelatch.getcount () > 0) { Stageprocessdonelatch.await (timeout, unit);}} catch (Interruptedexception e) {;}} Delegate.shutdown (timeout, unit);} @Overridepublic void Setnextpipe (pipe<?,? > Nextpipe) {delegate.setnextpipe (nextpipe);} /** * thread pool stop flag. Each Executorservice instance corresponds to a unique terminationtoken instance. This uses the idea of the two-phase * Termination mode (chapter 5th) to stop the thread pool instances that are shared by multiple pipe instances. * * @author viscent Huang * */private static class Terminationtoken extends Io.github.viscent.mtpattern.ch5.tpt.Terminati Ontoken{private final static concurrentmap<executorservice, terminationtoken> Instances_map = new Concurrenthashmap<executorservice, terminationtoken> ();//Private Constructor Private Terminationtoken () {}void Setistoshutdown ({This.toshutdown = true;} Static Terminationtoken newinstance (Executorservice executorserivce) {Terminationtoken token = Instances_map.get ( EXECUTORSERIVCE); if (null = = token) {token = new Terminationtoken (); Terminationtoken Existingtoken = instances_map.putifabsent (Executorserivce, token); if (null! = Existingtoken) {token = Existingtoken;}} return token;}}} </span>
Well, with the above definitions of the various classes, let us actually use the pipe line mode, this example is also a multi-threaded programming in the south of the book, is a thread pool-based pipeline example, but I have to hoist the gourd to add more than two pipeline, this example, Note that each pipe is threadpoolpipedecorator decorated before it is added to the pipe line. Oh, the task in the pipeline is the delegate thread pool to execute.
<span style= "FONT-SIZE:18PX;" >public class threadpoolbasedpipeexample{/** * Main function * * @param args * void * @author Lihong April 26, 2016 PM 2:4 3:54 * @since v1.0 */public static void Main (string[] args) {/* * Create thread pool */final threadpoolexecutor executorserivce;executor Serivce = new Threadpoolexecutor (1, Runtime.getruntime (). Availableprocessors () * 2, Timeunit.minutes, new Synchronousqueue<runnable> (), New Threadpoolexecutor.callerrunspolicy ());/* * Create a pipe line object */final simplepipeline <string, string> pipeline = new simplepipeline<string, string> ();/* * Create first pipeline */pipe<string, string> Pi PE = new abstractpipe<string, string> () {@Overrideprotected string doprocess (String input) throws pipeexception{ String result = input + "->[pipe1," + thread.currentthread (). GetName () + "]"; SYSTEM.OUT.PRINTLN (result); return result;}};/ * * Add the first pipe to the thread pool */pipeline.addasthreadpoolbasedpipe (pipe, EXECUTORSERIVCE);/* * Create a second pipe */pipe = new abstractpipe< String, String> (){@Overrideprotected string doprocess (String input) throws pipeexception{string result = input + "->[pipe2," + thread.cu Rrentthread (). GetName () + "]"; SYSTEM.OUT.PRINTLN (result); Try{thread.sleep (new Random (). Nextint (100)); catch (Interruptedexception e) {;} Return result;}};/ * * Add the second pipe to the pipe line */pipeline.addasthreadpoolbasedpipe (pipe, EXECUTORSERIVCE);/* * Create a third pipe */pipe = new abstractpipe< String, string> () {@Overrideprotected string doprocess (String input) throws pipeexception{string result = input + "-> ; [Pipe3, "+ thread.currentthread (). GetName () +"] ";; SYSTEM.OUT.PRINTLN (result); Try{thread.sleep (new Random (). Nextint (200)); catch (Interruptedexception e) {;} Return result;}};/ * * Add the third pipe to the pipe line */pipeline.addasthreadpoolbasedpipe (pipe, EXECUTORSERIVCE);/* * Fourth */pipe = new abstractpipe<string, String> () {@Overrideprotected string doprocess (String input) throws pipeexception{string result = input + "->[pipe4 , "+ Thread.CurrentThread (). GetName () +"] ";; SYSTEM.OUT.PRINTLN (Result); Try{thread.sleep (New Random (). Nextint (200));} catch (Interruptedexception e) {;} Return result;}};/ * * Add fourth pipe to Pipe line */pipeline.addasthreadpoolbasedpipe (pipe, EXECUTORSERIVCE);/* * Create fifth */pipe = new abstractpipe< String, string> () {@Overrideprotected string doprocess (String input) throws pipeexception{string result = input + "-> ; [Pipe5, "+ thread.currentthread (). GetName () +"] ";; SYSTEM.OUT.PRINTLN (result); Try{thread.sleep (new Random (). Nextint (200)); catch (Interruptedexception e) {;} return result;} @Overridepublic void shutdown (long timeout, timeunit unit) {//Closes the thread pool Executorserivce.shutdown () in the last pipe, try{ Executorserivce.awaittermination (timeout, unit);} catch (Interruptedexception e) {;}}};/ * * Add fifth pipe to Pipe line */pipeline.addasthreadpoolbasedpipe (pipe, EXECUTORSERIVCE);/* * Pipe line initialization */pipeline.init ( Pipeline.newdefaultpipelinecontext ()); int N = 100;try{for (int i = 0; i < N; i++) {pipeline.process ("task-" + i);}} catch (IllegalStateException e) {;} catch (Interruptedexception e) {;} Pipeline.shutdOwn (ten, Timeunit.seconds);}} </span>
Let's take a look at this demo and run the 5-task results
let's look at the execution of task four in detail:
<span style= "FONT-SIZE:18PX;" >task-4->[pipe1,pool-1-thread-5]task-4->[pipe1,pool-1-thread-5]->[pipe2,pool-1-thread-4]task-4- >[pipe1,pool-1-thread-5]->[pipe2,pool-1-thread-4]->[pipe3,pool-1-thread-3]task-4->[pipe1, pool-1-thread-5]->[pipe2,pool-1-thread-4]->[pipe3,pool-1-thread-3]->[pipe4,pool-1-thread-6]task-4- >[pipe1,pool-1-thread-5]->[pipe2,pool-1-thread-4]->[pipe3,pool-1-thread-3]->[pipe4,pool-1-thread-6 ]->[pipe5,pool-1-thread-1]</span>
As you can see from the code above, task-4 flows through 5 pipelines sequentially, but the specific processing actions in each pipeline are handled by different worker threads in the thread pool. The actions of task-4 in the 1th, 2, 3, 4, 55 pipelines are performed by thread pool worker threads 6, 4, 3, 6, 1, respectively. To sum up, a single task in pipeline mode is performed sequentially, but multiple tasks are performed concurrently with concurrency, because one phase of a task can proceed to the same stage of another task, even though both tasks are not completed. This eliminates the need to wait for all phases of a task to complete before another task can be processed
Java Multithreaded Programming Practical Guide (design mode, Huang Wenhai)-The Pipe line mode