Java Concurrency Basics (iii)

Source: Internet
Author: User

The 6th chapter begins with the second part, which explains structured concurrent applications, where most concurrent applications are constructed around "task execution", often with abstract and discrete units of work.

One, thread pool

Most server applications provide a natural task boundary: separate client requests as boundaries. Now that we are going to implement our own Web server, you must have seen this code:

class Singlethreadwebserver {    publicstaticvoidthrows  IOException {         New ServerSocket (+);          while (true) {            = socket.accept ();             // Processing Requests             HandleRequest (connection);     }}}

This serial method of performing a task is of course not possible, and it causes the program to lose its scalability. We will improve it:

classthreadpertaskwebserver{ Public Static voidMain (string[] args)throwsIOException {serversocket socket=NewServerSocket (80);  while(true) {            FinalSocket connection =socket.accept (); Runnable Task=NewRunnable () {@Override Public voidrun () {//Processing RequestsHandleRequest (connection);                        }            }; NewThread (Task). Start (); }    }}

We assign a thread to each task, but it still has a lot of problems, such as a very high thread life cycle, an excessive amount of resources, especially memory, an upper limit on the number of threads that can be created, and a OutOfMemoryError exception that can be thrown if exceeded. Before I read this book, I understood it at most. In fact, Java provides support for task execution, which is executor.

 Public Interface executor{    void  execute (Runnable command);}

Although executor is a simple interface, it provides the foundation for a flexible and powerful execution framework for asynchronous tasks. Now change the example above to a thread pool-based Web server:

classtaskexecutionwebserver{Private Static Final intNthreads = 100; Private Static FinalExecutor exec =Executors.newfixedthreadpool (nthreads);  Public Static voidMain (string[] args)throwsIOException {serversocket socket=NewServerSocket (80);  while(true) {            FinalSocket connection =socket.accept (); Runnable Task=NewRunnable () {@Override Public voidrun () {//Processing RequestsHandleRequest (connection);                            }            };        Exec.execute (Task); }    }}

Whenever you see this form of code:

New Thread (Task). Start ();

And when you want to get a more flexible execution strategy, consider using executor instead of thread.

Executor is based on the producer-consumer model. The thread that submits the task equals the producer, and the thread that performs the task is the consumer. The producer gives the task to the queue and the consumer gets the task execution from the queue. This decouples the task's submission process from the execution process.

Java provides a flexible thread pool and some useful default configurations. You can create by calling the static factory method in executors:

1. Newfixedthreadpool Newfixedthreadpool will create a fixed-length thread pool that creates a thread each time a task is committed, until the maximum number of thread pools is reached, The size of the thread pool will no longer change. (If a thread ends due to an unexpected exception, a new thread is added to the threads pool).

2,newcachedthreadpool Newcachedthreadpool will create a cacheable thread pool, if the current size of the thread pool exceeds the processing requirements, then the idle thread will be reclaimed, when the demand increases, New threads can be added, and there is no limit to the size of the thread pool.

3. Newsinglethreadexecutor Newsinglethreadexecutor is a single-threaded executor that creates a single worker thread to perform a task, and if this thread ends abnormally, Another thread is created instead. Newsingletheadexecutor ensures that tasks are executed serially in the order in which they are queued. (ex: FIFO, LIFO, priority)

4. Newscheduledthreadpool Newscheduledthreadpool creates a fixed-length thread pool and performs tasks in a deferred or timed manner.

Second, the life cycle of executor

  We already know how to create a executor, but how do we close it? Implementations of executor typically create threads to perform tasks. However, the JVM exits only after all (non-daemon) threads have been terminated. Therefore, if executor is not properly closed, the JVM will not end.

To address the life cycle of service execution, executor extends the Executorservice interface and adds some methods for life cycle management, as follows:

 Public Interface Implements executor{    void  shutdown ();    List<Runnable> shutdownnow ();     Boolean IsShutDown ();     Boolean isterminated ();     boolean awaittermination (longthrows  interruptedexception;}

The Executorservice life cycle has a status of 3: Run, off, terminated. The Executorservice is in the running state at the time of initial creation. The shutdown method performs a gentle shutdown process: No longer accepts new tasks, while waiting for the completed task to execute. The Shutdownnow method will perform a rough shutdown process: It will attempt to cancel all running tasks and no longer start a task in the queue that has not yet begun to execute.

We will then improve the Web server to a form that supports shutdown:

classlifecyclewebserver{Private FinalExecutorservice exec =Executors.newcachedthreadpool ();  Public voidStart ()throwsioexception{serversocket Socket=NewServerSocket (80);  while(!Exec.isshutdown ()) {            Try {                FinalSocket conn =socket.accept (); Exec.execute (NewRunnable () {@Override Public voidrun () {handlerequest (conn);            }                }); } Catch(rejectedexecutionexception e) {if(!Exec.isshutdown ()) {Log ("Task submission rejected", E); }            }        }    }         Public voidStop () {exec.shutdown (); }        voidhandlerequest (Socket connection) {Request req=readrequest (connection); if(Isshutdownrequest (req)) {stop (); }Else{dispatchrequest (req); }    }}

Iii. task of carrying results callable and future

The executor framework uses runnable as its basic task representation. Runnable is a very limited abstraction that cannot return a value or throw a checked exception. Callable is a better abstraction: it believes that the main entry point returns a value and may throw an exception. The future represents the life cycle of a task and provides a way to determine whether it has been completed or canceled, as well as the outcome of the task, or the cancellation of the task.

 Public InterfaceCallable<v>{V call ()throwsException;} Public InterfaceFuture<v>{    BooleanCancelBooleanmayinterruptifrunning); Booleaniscancelled (); BooleanIsDone (); V get ()throwsinterruptedexception,executionexception,cancellationexception; V Get (LongTimeout,timeunit unit)throwsinterruptedexception,executionexception,cancellationexception,timeoutexception;}

If the task is completed, then get will return or throw a exception immediately, and if the task is not completed, then get will block until the task is completed. If the task throws an exception, get encapsulates the exception as executionexception and re-throws it. If the task is canceled, then get will throw cannellationexception. If get throws a executionexception, it can be getcause to obtain the encapsulated initial exception. All methods in the Executorservice will return a future.

Example: Implementing a page renderer with the future

In order for the page renderer to achieve higher concurrency, the rendering process is first decomposed into two tasks, one rendering all the text, and the other downloading all the images. (Because one of the tasks is CPU intensive and the other task is I/O intensive, this approach can improve performance even on a single CPU system.) )

Pseudo code:

 Public classfuturerenderer{Private FinalExecutorservice executor =Executors.newcachedthreadpool (); voidrenderpage (charsequence source) {FinalList<imageinfo> Imageinfos =scanforimageinfo (source); Callable<List<ImageData>> task =NewCallable<list<imagedata>>() {@Override PublicList<imagedata> Call ()throwsException {List<ImageData> result =NewArraylist<imagedata>();  for(Imageinfo Imageinfo:imageinfos) {Result.add (Imageinfo.downloadimage ()); }                        returnresult;                        }                }; Future<List<ImageData>> future =Executor.submit (Task);                RenderText (source); Try{List<ImageData> ImageData =Future.get ();  for(ImageData data:imagedata) {renderimage (data); }        } Catch(interruptedexception e) {//Reset the interrupt state of a threadThread.CurrentThread (). interrupt (); //canceling a task because no results are requiredFuture.cancel (true); }Catch(executionexception e) {Throwlaunderthrowable (E.getcause ()); }    }}

A callable is created in futurerenderer to download all the images and submit them to a executorservice. This will return a future that describes the performance of the task. When the primary task requires an image, it waits for the result of the future.get call. If you're lucky, when the request starts, all the images have been downloaded, and even if not, at least the download task for the image has started earlier.

Problem: If the text is rendered much faster than the download image, then the final performance of the program differs greatly from the performance of the serial execution, and the code becomes more complex. Therefore, it is only when a large number of interdependent and isomorphic tasks can be processed concurrently that the actual performance gains from assigning the workload of the program to multiple tasks are reflected.

Workaround: Create a separate task for each image download and execute them in the thread pool. If you submit a set of tasks to the thread pool and want to get the results after the calculation is complete, you can keep the future associated with each calculation result, then use the Get method repeatedly, and then poll to determine whether the task is complete, but it is cumbersome. Fortunately, there is a better way: to complete the service (Completionservice).

Completionservice combines the functions of executor and blockingqueue. You can give the callable task to it for execution, and then use the take and poll methods similar to the queue operation to get the results that have been completed. Executorcompletionservice implements the Completionservice.

 Public classRender {Private FinalExecutorservice executor;  PublicRender (Executorservice executor) {Super();  This. Executor =executor; }        voidrenderpage (charsequence source) {List<ImageInfo> info =scanforimageinfo (source); Completionservice<ImageData> Completionservice =NewExecutorcompletionservice<imagedata>(executor); //submit a download task for each picture         for(Finalimageinfo Imageinfo:info) {Completionservice.submit (NewCallable<imagedata>(){                 PublicImageData Call () {returnImageinfo.downloadimage ();        }            }); }        //Loading ThisRenderText (source); Try {             for(intt = 0 t = info.size (); T < N; t++) {                //get the tasks you've completed, each time you get aFuture<imagedata> f =Completionservice.take (); ImageData ImageData=F.get ();            RenderImage (ImageData); }        } Catch(interruptedexception e) {//A thread break performs two operations: 1. Clears the interrupt state of the thread 2. Throw Interruptedexception//so the thread is still in a broken state after catching the exceptionThread.CurrentThread (). interrupt (); }Catch(executionexception e) {Throwlaunderthrowable (E.getcause ()); }    }}

Java Concurrency Basics (iii)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.