Completionservice Introduction
Completionservice and Executorservice can be used to perform a thread pool task, Executorservice inherits the executor interface, and Completionservice is an interface. So why Completionservice not directly inherit the executor interface? Primarily executor, the executor framework does not fully guarantee the asynchronous nature of task execution, that is, if you need to implement the asynchronous nature of a task, the asynchronous nature of the task is achieved as long as one thread is created for each task. Code is often included new Thread(task).start()
. The problem with this approach is that it has no limit on the number of threads that can be created (which can be limited in executorservice), but the biggest problem is that in the case of high concurrency, constantly creating threads that execute asynchronously will greatly increase the overhead of thread creation , Cause great resource consumption and affect the stability of the system . In addition, the executor framework supports the execution of synchronous tasks, which means that the run () method that invokes the commit task in the Execute method is a synchronous call.
In general, if you need to determine whether a task is completed, the idea is to get each future of the future list, and then call its Get method repeatedly, and set the timeout parameter to 0, so that the task is determined by polling the completion. Completionservice can be used to more precisely implement asynchronous execution of tasks and to accomplish asynchronous execution of tasks more easily.
Completionservice Implementation principle
Completionservice can actually be seen as a combination of executor and blockingqueue. Completionservice gets the results of the task execution through a put and take similar to Blockingqueue when it receives the task to perform. One realization of Completionservice is that executorcompletionservice,executorcompletionservice the specific computational tasks to executor completion.
On implementation, Executorcompletionservice creates a blockingqueue (using the linked list-based unbounded queue Linkedblockingqueue) in the constructor. The role of the blockingqueue is to preserve the results of executor execution. When the calculation is complete, call Futuretask's done method. When a task is submitted to Executorcompletionservice, the task is first packaged into Queueingfuture, which is a subclass of Futuretask and then overwrites the Futuretask done method, The results of executor execution are then put into blockingqueue. The source code of Queueingfuture is as follows:
privateclass QueueingFuture extends FutureTask<Void> { QueueingFuture(RunnableFuture<V> task) { supernull); this.task = task; } protectedvoiddone() { completionQueue.add(task); } privatefinal Future<V> task; }
As you can see from the code, Completionservice converts the submitted task to queueingfuture and overwrites the Done method, in which the task is added to the task queue. This is consistent with the previous analysis of the executor framework.
Using Executorservice to implement tasks
The code simulates the operation of loading the goods in the e-commerce, because of the variety of commodity attributes, it is possible to set the display of a product's picture and the description of the product to two independently executed tasks. In addition, because the picture of the product may have many sheets, the picture is often displayed more slowly than the profile. At this point, asynchronous execution can speed up execution to some extent to improve the performance of the system. The following code demonstrates this:
Packagecom.rhwayfun.patchwork.concurrency.r0410;ImportJava.text.DateFormat;ImportJava.text.SimpleDateFormat;ImportJava.util.ArrayList;ImportJava.util.Date;ImportJava.util.List;Importjava.util.concurrent.*;/** * Created by Rhwayfun on 16-4-10. * * Public class displayproductinfowithexecutorservice { //thread pool Private FinalExecutorservice Executorservice = Executors.newfixedthreadpool (2);//Date Formatter Private FinalDateFormat format =NewSimpleDateFormat ("HH:mm:ss");//Analog e-commerce website Product details information Display //Because there may be many pictures of a picture of a product, there will always be a delay in displaying a picture of the product. //In addition to the details of the product also includes a product introduction and other information such as the display, because the main message here is the text for //Master, so it can be displayed faster than the picture. The following code is the main line to perform these two tasks, end //into the execution of these two tasks. Since there is a big gap between the two tasks, the first //idea is to execute asynchronously, first perform the download task of the image, and then (not long) start to execute the product //Introduction information display, if the network is good enough, the picture is not very big case, may start to show //Product When the image is downloaded, so naturally think of using executor and callable to complete the different //Step task execution. Public void Renderproductdetail() {Finallist<productinfo> Productinfos = Loadproductimages ();//tasks for downloading images asynchronouslycallable<list<productimage>> task =NewCallable<list<productimage>> () {@Override PublicList<productimage>Pager()throwsException {list<productimage> imageList =NewArraylist<> (); for(ProductInfo Info:productinfos) {Imagelist.add (Info.getimage ()); }returnImageList; } };//Submit to thread pool executionfuture<list<productimage>> listfuture = executorservice.submit (Task);//Display the information of the product introductionRenderproducttext (Productinfos);Try{//Display pictures of productslist<productimage> imageList = Listfuture.get (); Renderproductimage (imageList); }Catch(Interruptedexception e) {//Reset the interrupt state of the thread if the display picture breaks abnormally //This allows the thread in wait to wake upThread.CurrentThread (). interrupt ();//Cancel the execution of the task at the same time, the parameter false indicates that the thread is executing uninterruptedListfuture.cancel (true); }Catch(Executionexception e) {Try{Throw NewThrowable (E.getcause ()); }Catch(Throwable throwable) {Throwable.printstacktrace (); } } }Private void Renderproductimage(list<productimage> imageList) { for(Productimage image:imagelist) {Try{TimeUnit.SECONDS.sleep (1); }Catch(Interruptedexception e) {E.printstacktrace (); }} System.out.println (Thread.CurrentThread (). GetName () +"Display Products images!"+ Format.format (NewDate ())); }Private void Renderproducttext(list<productinfo> Productinfos) { for(ProductInfo Info:productinfos) {Try{Thread.Sleep ( -); }Catch(Interruptedexception e) {E.printstacktrace (); }} System.out.println (Thread.CurrentThread (). GetName () +"Display Products description!"+ Format.format (NewDate ())); }PrivateList<productinfo>loadproductimages() {list<productinfo> List =NewArraylist<> ();Try{TimeUnit.SECONDS.sleep (5); }Catch(Interruptedexception e) {E.printstacktrace (); } ProductInfo info =NewProductInfo (); Info.setimage (NewProductimage ()); List.add (info); System.out.println (Thread.CurrentThread (). GetName () +"Load Products info!"+ Format.format (NewDate ()));returnList }/** * Products * * Private Static class productinfo{ PrivateProductimage image; PublicProductimageGetImage() {returnImage } Public void setimage(productimage image) { This. image = Image; } }Private Static class productimage{} Public Static void Main(string[] args) {Displayproductinfowithexecutorservice cd =NewDisplayproductinfowithexecutorservice (); Cd.renderproductdetail (); System.exit (0); }}
The result of the code execution is as follows:
In the above code, the execution of the task of downloading and introducing the product image is attempted in parallel, although this method can accomplish the task, but the parallelism of the heterogeneous task will improve the performance in a limited way. Consider an extreme situation where the download speed of a product picture is much less than the load of the introductory information, so this is the case (usually the ratio of loading speed to a larger value) is virtually the execution efficiency of the task's serial. And with more complex code, the gains are so small. The improvement in system performance is significant only if the equivalent is independent and homogeneous tasks can be processed concurrently (because loading pictures and introduction execution speed varies too much, so it is not a homogeneous task).
Using Completionservice to implement tasks
One of the major improvements to using Completionservice is to distribute multiple images to multiple units of work, so that the gap between the loading of a product picture and the speed at which it is loaded is reduced by distribution, allowing these small tasks to execute in the thread pool, This greatly reduces the time to download all the pictures, so at this time you can think that the two tasks are isomorphic. It's best to use Completionservice to do it right.
Packagecom.rhwayfun.patchwork.concurrency.r0410;ImportJava.text.DateFormat;ImportJava.text.SimpleDateFormat;ImportJava.util.ArrayList;ImportJava.util.Date;ImportJava.util.List;Importjava.util.concurrent.*;/** * Created by Rhwayfun on 16-4-10. * * Public class displayproductinfowithcompletionservice { //thread pool Private FinalExecutorservice Executorservice;//Date Formatter Private FinalDateFormat format =NewSimpleDateFormat ("HH:mm:ss"); Public Displayproductinfowithcompletionservice(Executorservice Executorservice) { This. Executorservice = Executorservice; } Public void Renderproductdetail() {Finallist<productinfo> Productinfos = Loadproductinfos (); Completionservice<productimage> Completionservice =NewExecutorcompletionservice<productimage> (Executorservice);//Create a work task for each image download for(FinalProductInfo Info:productinfos) {completionservice.submit (NewCallable<productimage> () {@Override PublicProductimagePager()throwsException {returnInfo.getimage (); } }); }//Display the information of the product introductionRenderproducttext (Productinfos);Try{//Show product pictures for(inti =0, n = productinfos.size (); I < n; i++) {future<productimage> imagefuture = Completionservice.take (); Productimage image = Imagefuture.get (); Renderproductimage (image); } }Catch(Interruptedexception e) {//Reset the interrupt state of the thread if the display picture breaks abnormally //This allows the thread in wait to wake upThread.CurrentThread (). interrupt (); }Catch(Executionexception e) {Try{Throw NewThrowable (E.getcause ()); }Catch(Throwable throwable) {Throwable.printstacktrace (); } } }Private void Renderproductimage(productimage image) {Try{Thread.Sleep ( -); }Catch(Interruptedexception e) {E.printstacktrace (); } System.out.println (Thread.CurrentThread (). GetName () +"Display Products images!"+ Format.format (NewDate ())); }Private void Renderproducttext(list<productinfo> Productinfos) { for(ProductInfo Info:productinfos) {Try{Thread.Sleep ( -); }Catch(Interruptedexception e) {E.printstacktrace (); }} System.out.println (Thread.CurrentThread (). GetName () +"Display Products description!"+ Format.format (NewDate ())); }PrivateList<productinfo>Loadproductinfos() {list<productinfo> List =NewArraylist<> ();Try{TimeUnit.SECONDS.sleep (3); }Catch(Interruptedexception e) {E.printstacktrace (); } ProductInfo info =NewProductInfo (); Info.setimage (NewProductimage ()); List.add (info); System.out.println (Thread.CurrentThread (). GetName () +"Load Products info!"+ Format.format (NewDate ()));returnList }/** * Products * * Private Static class productinfo { PrivateProductimage image; PublicProductimageGetImage() {returnImage } Public void setimage(productimage image) { This. image = Image; } }Private Static class productimage {} Public Static void Main(string[] args) {Displayproductinfowithcompletionservice cd =NewDisplayproductinfowithcompletionservice (Executors.newcachedthreadpool ()); Cd.renderproductdetail (); }}
The execution results are the same as above. Because multiple Executorcompletionservice can share a single executor, you can create a private, shared executor executorcompletionservice that is specific to a particular calculation.
Completionservice Summary
- Performs asynchronous tasks more precisely and easily than Executorservice,completionservice
- An implementation of Completionservice is Executorcompletionservice, which is a fusion of executor and Blockingqueue functions, executor the task of computing, Blockingqueue is responsible for saving the execution results of the asynchronous task
- When performing a large number of mutually independent and isomorphic tasks, you can use the Completionservice
- Completionservice can set a time limit for the execution of a task, mainly by Blockingqueue the poll (long Time,timeunit unit) to achieve a time limit for the task execution result, and cancel the task if it is not completed
Java Concurrency Programming series 28: Completionservice