Thinking in Java --- correct termination of sub-threads

Source: Internet
Author: User

Thinking in Java --- correct termination of sub-threads

During multi-threaded programming, we often start multiple sub-threads to complete a task, so how to exit these threads correctly when the task is completed or when the task is running to a certain extent. We will discuss this issue below.

I. Termination of a non-blocking task
The termination of a non-blocking task is the simplest case. In this case, we can easily terminate the task correctly by setting the condition for the while () loop end in the run () method. The following code demonstrates how to terminate a non-blocking task:

Package lkl; import java. util. concurrent. executorService; import java. util. concurrent. executors; import java. util. concurrent. timeUnit;/** start three threads at the same time to count a counting object, * When the Count reaches one hundred, all sub-threads terminate * // counting class count {private int Count = 0; // self-incrementing function public synchronized void increment () {count ++;} public synchronized int get () {return count ;}} class Task implements Runnable {private Count count; // The identifier used to determine whether the Task is to end, is a variable that needs to be read by multiple threads. // Use the volatile key to modify the variable. volatile can ensure the visibility of the variable. // after any task modifies canceled, this change is visible to all tasks. public static volatile boolean canceled = false; // modify the canceled flag to public static void cancel () {canceled = true;} public Task (Count count) {this. count = count;} // each task continuously calls the count increment () method. When the value is equal to 100, all tasks exit the public void run () {while (! Canceled) {// you must ensure that the following code is an atomic operation to exit synchronized (count) correctly when it is added to 100) {// if (canceled) {// This synchronization block is not added, if (canceled) {// After the above synchronization is added, the return;} count. increment (); // System. out. println (count. get (); if (count. get () = 100) {cancel () ;}try {TimeUnit. MILLISECONDS. sleep (100);} catch (InterruptedException ex) {System. out. println ("interrupt") ;}}} public class Test {public static void main (String [] args) throws Exception {ExecutorService exec = Executors. newCachedThreadPool (); Count count = new Count (); // start three threads at the same time to Count (int I = 0; I <3; I ++) {exec.exe cute (new Task (count);} exec. shutdown (); TimeUnit. SECONDS. sleep (4); System. out. println (count. get (); System. exit (1 );}}

This code is very simple, but pay attention to the synchronization block we add in the while loop. If the synchronization is not performed in that place, although the increment method of the count object and get () the methods are all synchronous, but because the two statements still have switching between them, the task cannot be ended exactly when it reaches 100 in most cases; at the same time, you should also note that there will be a check before each increment, which is also necessary, because although some threads set the canceled flag to true, but the current thread may just wake up from the blocking status, you must execute all the following statements to check whether the while loop is exited. This will cause more problems.

Ii. Termination of blocking tasks
It is much more difficult to terminate a blocking task than a non-blocking task. When a thread is blocked, it stops at a certain point in the run () method, so we cannot automatically jump out of the loop by checking a condition by the program itself as above. We can roughly classify blocking into three categories: Blocking caused by calling sleep (), blocking caused by waiting for synchronization locks, and blocking caused by waiting for a certain resource (such as IO blocking ). The Thread class provides an interrupt () method for interrupt blocking. This method has two functions: the first is to generate an InterruptedException, and the other is to reset the Thread's interrupted status to true. However, it is a pity that this interruption is only effective for the blocking caused by sleep () and is invalid for the other two blocking methods. The following code demonstrates this.

Package lkl; import java. io. IOException; import java. io. inputStream; import java.net. serverSocket; import java.net. socket; import java. util. concurrent. executorService; import java. util. concurrent. executors; import java. util. concurrent. future; import java. util. concurrent. timeUnit;/** demonstrate how to use the Thread. the interrupt () method terminates three different blocking scenarios * // The blocked class SleepBlocked implements Runnable {public void run () {try {TimeUnit. SECONDS. sleep (1000);} catch (InterruptedException e) {System. out. println ("InterruptedException");} System. out. println ("Exiting SleepBlocked. run () ") ;}}// IO blocking class IOBlocked implements Runnable {private InputStream in; public IOBlocked (InputStream is) {in = is;} public void run () {try {System. out. println ("waiting for read ():"); in. read ();} catch (IOException e) {if (Thread. currentThread (). isInterrupted () {System. out. println ("Interrupted from blocked I/O");} else {throw new RuntimeException () ;}} System. out. println ("Exiting IOBlocked. run () ") ;}}// synchronization lock blocking class SynchronizedBlocked implements Runnable {public synchronized void f () {while (true) {Thread. yield () ;}} public SynchronizedBlocked () {new Thread () {// start a Thread and obtain its own lock public void run () by executing a never-Quit Synchronization Method () {f ();}}. start ();} public void run () {System. out. println ("Trying to call f ()"); f (); System. out. println ("Exiting synchronizedBlocked. run () ") ;}} public class Interrupting {private static ExecutorService exec = Executors. newCachedThreadPool (); public static void test (Runnable r) throws InterruptedException {Future f = exec. submit (r); TimeUnit. MICROSECONDS. sleep (1, 100); System. out. println ("Interrupting" + r. getClass (). getName (); f. cancel (true); // end the thread System by using the cancel () method. out. println ("Interrupt sent to" + r. getClass (). getName ();} public static void main (String [] args) throws Exception {ExecutorService exec = Executors. newCachedThreadPool (); ServerSocket server = new ServerSocket (8080); InputStream socketInput = new Socket ("localhost", 8080 ). getInputStream (); exec.exe cute (new IOBlocked (socketInput); exec.exe cute (new IOBlocked (System. in); TimeUnit. MILLISECONDS. sleep (1, 100); System. out. println ("Shutting down all threads"); exec. shutdownNow (); // send the interrupted message TimeUnit to all threads. SECONDS. sleep (1); System. out. println ("Closing" + socketInput. getClass (). getName (); socketInput. close (); // end the thread TimeUnit by closing the underlying blocked IO. SECONDS. sleep (1); System. out. println ("Closing" + System. in. getClass (). getName (); System. in. close (); System. out. println ("end") ;}}/* outPutwaiting for read (): waiting for read (): Shutting down all threadsClosing java.net. socketInputStreamInterrupted from blocked I/OExiting IOBlocked. run () Closing java. io. bufferedInputStreamend */

From the output, we can see that only the blocking caused by sleep () is interrupted by calling the Thread. interrupt () method, and the other two blocking methods cannot be interrupted by this method. But there are exceptions.
1) For IO blocking, as shown above, we can definitely stop the blocking by shutting down the underlying IO resources. However, if we use nio, It can automatically respond to interrupt () interruption. The following code is used:

Package lkl; import java. io. IOException; import java.net. inetSocketAddress; import java.net. serverSocket; import java. nio. byteBuffer; import java. nio. channels. asynchronousCloseException; import java. nio. channels. closedByInterruptException; import java. nio. channels. socketChannel; import java. util. concurrent. executorService; import java. util. concurrent. executors; import java. util. concurrent. future; import java. util. concurrent. timeUnit;/** demonstrate how to use interrupt to interrupt nio blocking */class NIOBlocked implements Runnable {private final SocketChannel SC; public NIOBlocked (SocketChannel SC) {this. SC = SC;} public void run () {try {System. out. println ("Waiting for read () in" + this); SC. read (ByteBuffer. allocate (1); // blocking here} catch (ClosedByInterruptException e) {System. out. println ("ClosedByInterruptedException");} catch (AsynchronousCloseException e) {System. out. println ("AsynchronousCloseException");} catch (IOException e) {throw new RuntimeException (e);} System. out. println ("Exiting NIOBlocked. run () "+ this) ;}} public class NIOInterruption {public static void main (String [] args) throws IOException, InterruptedException {ExecutorService exec = Executors. newCachedThreadPool (); ServerSocket server = new ServerSocket (8080); InetSocketAddress isa = new InetSocketAddress ("localhost", 8080); SocketChannel sc1 = SocketChannel. open (isa); SocketChannel sc2 = SocketChannel. open (isa); Future f = exec. submit (new NIOBlocked (sc1); exec.exe cute (new NIOBlocked (sc2); exec. shutdown (); // exec. shutdownNow () // you can use this sentence to end the TimeUnit of the subthread correctly. SECONDS. sleep (1); // The following two methods are used: f. cancel (true); // exit blocking TimeUnit by generating InterruptedException interruption. SECONDS. sleep (1); sc2.close (); // You can manually close the underlying resource to interrupt blocking. In contrast to the preceding method,}/* outPut Waiting for read () inlkl. NIOBlocked @ 3801167 aWaiting for read () inlkl. NIOBlocked @ 601a013bClosedByInterruptedExceptionExiting NIOBlocked. run () lkl. NIOBlocked @ 601a013bAsynchronousCloseExceptionExiting NIOBlocked. run () lkl. NIOBlocked @ listen 1167a */

2) for synchronization lock blocking, if we use ReentrantLock, it can also be interrupted, which is different from the blocking caused by synchronized. The following code demonstrates this:

Package lkl; import java. util. concurrent. timeUnit; import java. util. concurrent. locks. reentrantLock;/** displays the blocking caused by locking with ReentrantLock. * You can use Thread. interruted () method to exit **/class BlockedMutex {private ReentrantLock lock = new ReentrantLock (); public BlockedMutex () {lock. lock (); // lock yourself in the constructor, and do not release} public void f () {try {// when this thread gets the lock, this method returns lock immediately. lockInterruptibly (); System. out. println ("lock acquired in f ()");} catch (InterruptedException ex) {System. out. println ("Interrupted from lock acquisition in f ()") ;}} class Blocked2 implements Runnable {BlockedMutex blocked = new BlockedMutex (); public void run () {System. out. println ("Waiting for f () in BlockedMutex"); blocked. f (); System. out. println ("Broken out of blocked call") ;}} public class Interrupting2 {public static void main (String [] args) throws Exception {Thread t = new Thread (new Blocked2 (); t. start (); TimeUnit. SECONDS. sleep (1); System. out. println ("t. interrupted "); t. interrupt () ;}}/* outPutWaiting for f () in BlockedMutext. interruptedInterrupted from lock acquisition in f () Broken out of blocked call */

3. A unified representation
We know that the Thread's interrupt () method can not only throw an InterruptedException exception, but also reset the Thread's interrupt status. We can use interrupted () to check whether the interrupt () method has been called; therefore, we can check the interrupted () status to determine whether to exit the subthread. This method can be used in combination with the method for capturing interrupt exceptions. It is worth noting that the class designed to respond to interrupt () must have a corresponding resource clearing mechanism. The following code is used:

Package lkl; import java. util. concurrent. timeUnit;/** use the Interrupted () of the thread to control the end Of the subthread * // simulate a class NeedsCleanup {private final int id of the object to be cleared after allocation; public NeedsCleanup (int ident) {id = ident; System. out. println ("NeedsCleanup" + id);} public void cleanup () {System. out. println ("Cleaning up" + id) ;}} class Blocked3 implements Runnable {private volatile double d = 0.0; public void run () {try {while (! Thread. interrupted () {// For the object to be cleaned up, it must be followed by the try finally statement NeedsCleanup clean1 = new NeedsCleanup (1); try {System. out. println ("Sleeping"); TimeUnit. MILLISECONDS. sleep (500); NeedsCleanup clean2 = new NeedsCleanup (2); try {System. out. println ("Calculating"); for (int I = 1; I <250000000; I ++) {d = d + (Math. PI + Math. e)/d;} System. out. println ("Finished time-consuming operation");} finally {clean2.cleanup () ;}} finally {clean1.cleanup () ;}} System. out. println ("Exiting by Interrupted");} catch (InterruptedException ex) {System. out. println ("Exiting by InterruptedException") ;}} public class Interrupted3 {public static void main (String [] args) throws Exception {Thread t = new Thread (new Blocked3 (); t. start (); TimeUnit. MILLISECONDS. sleep (1800); t. interrupt () ;}}/* NeedsCleanup 1 SleepingNeedsCleanup 2 CalculatingFinished time-consuming operationCleaning up 2 Cleaning up 1 Exiting by Interrupted */

In short, Use Thread. interrupted () is a good choice to control the Thread loop, because we can always use Thread. interrupt () to terminate this Thread (including the shutdownNow () method of ExecutorService and the cancel () method of the Thread); if there is no loop in the Thread, only blocking occurs, in most cases, Thread. interrupt () can also interrupt these blocking and throw an InterruptedException exception.

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.