Java concurrent programming Series 29: correct termination and restoration of threads (continued)

Source: Internet
Author: User
Tags dateformat thread stop

Java concurrent programming Series 29: correct termination and restoration of threads (continued)

Recognition interruption

Previously, in the correct termination and recovery Thread article, we introduced the interrupt method of the Thread class and the termination of the Thread using the flag. As we have briefly introduced the default interrupt method of jdk, we have not introduced the thread interrupt mechanism in detail. To terminate a thread correctly, it is necessary to deeply understand the nature of thread interruption. Java does not provide a preemptive and secure interrupt mechanism, but Java provides a thread collaboration mechanism (previously mentioned interrupt methods and flag spaces are essentially a means of collaboration between threads ), however, an interrupt mechanism is provided, which allows one thread to terminate the current work of another thread. Therefore, you need to consider the interrupt location and timing during program design.

Return to the example where the volatile flag is used to terminate a thread, and call the cancel Method in the code to cancel the I auto-increment request. If the Runner thread is executed next time, or, when you are about to execute the next auto-increment request, determine whether the value of on is changed to false. If so, the execution is terminated.

According to the running result, the Count task of the Runner is canceled and then exited. Before the Runner thread finally cancels the execution, there will be a certain amount of time. If the task that calls this method calls a method that will block, such as the put Method of BlockingQueue, this task may always violate the law and the on value is changed to false, so the Runner thread will not terminate.

Example

For example, the following code illustrates this point:

Package com. rhwayfun. patchwork. concurrency. r0411; import java. math. bigInteger; import java. util. concurrent. blockingQueue; import java. util. concurrent. linkedBlockingQueue; import java. util. concurrent. timeUnit;/*** Created by rhwayfun on 16-4-11. */public class BrokenShutdownThread extends Thread {// indicates whether to continue running. private static volatile boolean on = true; // blocked queue private final BlockingQueue
  
   
Queue; public BrokenShutdownThread (BlockingQueue
   
    
Queue) {this. queue = queue;} public void run () {try {BigInteger p = BigInteger. ONE; while (on) {// The producer can put 40 numbers at a time for (int I = 0; I <40; I ++) {queue. put (p = p. nextProbablePrime (); System. out. println (Thread. currentThread (). getName () + ": put value" + p) ;}} catch (InterruptedException e) {}} public void cancel () {on = false ;} /*** Consumer Thread */static class Consumer extends Thread {// blocking queue private final BlockingQueue
    
     
Queue; public Consumer (BlockingQueue
     
      
Queue) {this. queue = queue;} @ Override public void run () {try {while (on) {// the consumer can only consume one number of systems at a time. out. println (Thread. currentThread (). getName () + ": get value" + queue. take ();} System. out. println ("work done! ");} Catch (InterruptedException e) {e. printStackTrace () ;}} public static void main (String [] args) throws InterruptedException {BlockingQueue
      
        Queue = new LinkedBlockingQueue <> (5); BrokenShutdownThread producer = new BrokenShutdownThread (queue); // start the counting thread producer. start (); TimeUnit. SECONDS. sleep (1); new Consumer (queue ). start (); TimeUnit. SECONDS. sleep (1); producer. cancel ();}}
      
     
    
   
  

Run the above program and find that although the console outputswork done!But the program is still not stopped. After careful analysis, we will find that the producer's speed (40 times/time) is much higher than the consumer's speed (1 time/time), resulting in the queue being filled up, the put method is blocked. Although the volatile variable on is set to false by calling the cancel method one second after running, the put Method of the producer thread cannot be recovered from the blocked put method because it is blocked, the natural program cannot be terminated.

Recognition interruption

Each thread has a boolean interrupt state. When the thread is interrupted, the interrupt status is set to true. Different interrupt operations can be performed using three methods of Thread:

public void interrupt() {...}public static boolean interrupted() {...}public boolean isInterrupted() {...}

The interrupt method can interrupt the thread, interrupted can clear the thread interruption status, and the isInterrupted method can return the interruption status of the current thread.

When a thread calls a method that will be blocked, such as wait () and sleep (), the thread checks its interrupt status and returns the result in advance in case of interruption. Which of the following blocking methods can respond to interrupted operations?Clears the interrupt status and throws InterruptedException.. The purpose of throwing InterruptedException is to indicate that the thread needs to end early due to interruption. The essence of calling the interrupt method for execution interruption isCalling the interrupt method does not immediately stop the work being executed by the target thread, but only transmits the message of request interruption. Then the thread will interrupt itself at the next time.. InterruptedException is thrown when an interrupt request is received, giving the thread the freedom to choose an interrupt policy. Generally, the calling code requires additional processing on the thrown InterruptedException. It is incorrect to directly block this exception (that is, to directly call the printStackTrace () method ). The consequence of blocking interrupt exceptions is that the upper layer of the call stack cannot respond to the interrupt request.

Correction of the above Code

Based on the above analysis, you only need to make the following changes to the Code to terminate the thread correctly:

Public void run () {try {BigInteger p = BigInteger. ONE; while (on &&! Thread. currentThread (). isInterrupted () {// The producer can put 40 numbers at a time for (int I = 0; I <40; I ++) {queue. put (p = p. nextProbablePrime (); System. out. println (Thread. currentThread (). getName () + ": put value" + p) ;}} catch (InterruptedException e) {// Let the thread exit return ;}} public void cancel () {on = false; interrupt ();} static class Consumer extends Thread {// blocking queue private final BlockingQueue
       
        
Queue; public Consumer (BlockingQueue
        
         
Queue) {this. queue = queue;} @ Override public void run () {try {while (on &&! Thread. currentThread (). isInterrupted () {// the consumer can only consume 1 number at a time. System. out. println (Thread. currentThread (). getName () + ": get value" + queue. take ();} System. out. println ("work done! ");} Catch (InterruptedException e) {Thread. currentThread (). interrupt ();}}}
        
       

The other code remains unchanged. After running the above program again, it is found that the program can be terminated correctly. It is mainly to use the interrupt mechanism to complete the collaboration between threads, so as to terminate threads correctly.

In fact, InterruptedException thrown when a method that can be blocked is called to enable the caller to notice the interrupt information, so that the caller can interrupt his/her own operations. It is often necessary to perform other operations before sending the interrupt information to the caller. If the thread uses the interrupt mechanism to complete the collaboration between threads, it should callThread. currentThread (). intrrupt () restores the interruption status of the current ThreadIn this way, the current thread can continue other operations. Under normal circumstances, you need to respond to the interruption, unless you have implemented the operation that the interruption should be performed.

In addition to the previous method, you can also use Future to cancel thread execution. get (Long time, TimeUnit unit) methods with time-out restrictions cancel thread execution. If the task is not completed within the specified time, you can directly call Future in the code. the cancel () method cancels the task execution. There are two scenarios for canceling a task: one is that the task is completed at the specified time, and the call to cancel the task has no effect; the other is that the task is not completed at the specified time, after the cancel method is called, the task will be interrupted.

The pseudocode is as follows:

Future task = threadPool. submit (runnable); try {} catch (TimeOutException e) {// cancels the execution of the task} catch (ExecutionException e) {// If an execution exception is thrown in the task, throw the exception throw (new Throwable (e. getCause ();} finally {// true indicates that the task being executed can receive interruptions. if the task is being executed, the thread can be interrupted. // if the value is false, if the task is not started, do not start the task. cancel (true );}

Complete example of thread Cancellation

Here we use Log service as an example. The business scenario is as follows: at the front end, multiple producers call the logs of the log service output program, and the producer puts the logs to be output into a queue, the backend server has a consumer thread that extracts and outputs log information from the queue (the destination may be different ). Obviously this is a typical producer-consumer problem, but there are multiple producers, but there is only one consumer. Obviously, if the producer's speed is much higher than the consumer's processing speed, blocking may occur, but this has been solved in the above analysis. Now, you need to provide a reliable method to disable Log service. You can call the service interface on the frontend to stop Log service correctly without any problems.

The implementation code is as follows:

Package com. rhwayfun. patchwork. concurrency. r0411; import java. io. printWriter; import java. text. dateFormat; import java. text. simpleDateFormat; import java. util. date; import java. util. concurrent. blockingQueue; import java. util. concurrent. linkedBlockingQueue; import java. util. concurrent. timeUnit;/*** Created by rhwayfun on 16-4-11. */public class LoggerService {// blocking queue for storing log messages private final BlockingQueue
         
          
LogQueue; // The Consumer thread that prints logs private final LoggerThread loggerThread; // The Print Server private PrintWriter writer that prints logs; // The private boolean isShutdown sign indicating whether Log service is disabled; // The counter private int reservations of the caller executing the log method; public LoggerService (PrintWriter writer) {this. logQueue = new LinkedBlockingQueue <> (5); this. loggerThread = new LoggerThread (writer);}/*** start Log Service */public void start () {loggerThread. start ();}/*** record day Log ** @ param msg * @ throws InterruptedException */public void recordLog (String msg) throws InterruptedException {// logs are added with conditions. // when a close request is received, the log synchronized (this) is stopped in the queue. {if (isShutdown) throw new IllegalStateException ("LoggerService is shutdown! "); ++ Reservations;} // The producer puts the message into the queue. // the synchronized block is not put here because the put method blocks logQueue. put (msg);}/*** stop Log Service */public void stop () {// check and close the request synchronized (this) {isShutdown = true ;} // Let the consumer thread stop retrieving logs from the queue loggerThread. interrupt ();}/*** consumer Thread */private class LoggerThread extends Thread {private PrintWriter writer; public LoggerThread (PrintWriter writer) {this. writer = writer ;}@ Override public void run () {try {while (true) {try {// The lock held is the same as the previous one. // if the application closing request is received and no producer thread continues to enter logs in the queue, the loop ends, the consumer thread terminates synchronized (LoggerService. this) {if (isShutdown & reservations = 0) break;} // obtain the producer's log from the queue String msg = logQueue. take (); // every time a log is output, one thread is reduced to synchronized (LoggerService. this) {-- reservations;} writer. println ("Read:" + msg);} catch (InterruptedException e) {// restore the Thread in the interrupted state. currentThread (). interrupt () ;}}finally {writer. close () ;}}/ ***** producer thread */private static class LoggerWriter implements Runnable {private LoggerService service; private final DateFormat format = new SimpleDateFormat ("HH: mm: ss "); public LoggerWriter (LoggerService service) {this. service = service ;}@ Override public void run () {try {String msg = "time is" + format. format (new Date (); System. out. println ("Write:" + msg); service. recordLog (msg);} catch (InterruptedException e) {Thread. currentThread (). interrupt () ;}} public static void main (String [] args) throws InterruptedException {LoggerService service = new LoggerService (new PrintWriter (System. out); // create multiple producer threads to create logs for (int I = 0; I <5; I ++) {new Thread (new LoggerWriter (service )). start (); TimeUnit. SECONDS. sleep (1);} // start Log service. start (); // sleep for 10 seconds TimeUnit. SECONDS. sleep (10); // disable Log service. stop ();}}
         

 

Summary

Java does not provide a preemptible mechanism for safely terminating threads. However, the thread interruption mechanism can be used to terminate threads well. In addition to the flag, the FutureTask and Executor frameworks can also terminate threads, the cancel Method of FutureTask is used here. Unless you implement the interrupt policy in the program, do not block the interrupt exception. The purpose of throwing InterruptedException is to enable the upper-layer caller to receive the interrupt information, and perform operations on the interrupt. If you need to perform other operations before passing the interrupt information to the upper-layer caller, you need to call Thread.currentThread().interrupt()Restore the interruption status of the current thread. If you use the thread pool to execute tasks, you can use its shutdown method or the shutdownNow method to terminate the thread.

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.