0 foreword

When a thread is created and started, it is neither in the execution state as soon as it is started nor always in the execution state. In its life cycle, a thread passes through five states: New, Runnable, Running, Blocked, and Dead. Especially when a thread is started, it cannot always “hog” the CPU and run on its own, so the CPU has to switch between threads, so the thread state will switch between running and blocking many times.

1 Create (New) state

When a thread is created using the new keyword, the thread is created as follows:

  1. The JVM allocates memory for it and initializes the values of its member variables.
  2. At this time, the thread object does not show any dynamic characteristics of the thread, and the program will not execute the thread execution body of the thread.

2 Runnable status

When the thread object calls the start() method, the thread is in the ready state. The thread status is as follows:

  1. The JVM creates a method call stack and a program counter for it;
  2. The thread in this state is always in a thread-ready queue (albeit in a queue form, in fact, called a runnable pool rather than a runnable queue). Because CPU scheduling is not necessarily in first-in, first-out order), threads are not running;
  3. At this point, the thread waits for the system to allocate CPU time slices for it, rather than executing the start() method immediately.

Call the start() and run() methods as follows:

  1. The start() method is called to start the thread, and the run() method is treated as the thread body. But if you call the run() method of a thread object directly, the run() method is executed immediately and no other thread can execute concurrently until the run() method returns. That is, the system treats the thread object as a normal object, and the run() method is a normal method, not the thread body;
  2. Note that the thread is no longer in the new state after calling the run() method of the thread. Do not call the start() method of the thread object again. Only to in the new state of the thread calls the start () method, otherwise will cause IllegaIThreadStateExccption exception;

How to tell a child thread to execute immediately after calling the start() method instead of “waiting to execute” :

A program can use thread.sleep (1) to put the current running Thread (the main Thread) to sleep for 1 millisecond. 1 millisecond is sufficient, because the CPU will not be idle during this millisecond, and it will execute another Thread that is in the ready state, so that the child Thread can start executing immediately.

3 Running status

A thread is running when the CPU begins to schedule a thread in the ready state, which has received the CPU time slice before it can actually start executing the thread body of the run() method.

  1. If the computer has only one CPU, only one thread is running at any one time;
  2. If on a multi-processor machine, there will be multiple threads running in parallel;
  3. When the number of threads is greater than the number of processors, there will still be multiple threads rotating on the same CPU.

Threads in the running state of the most complex, it can’t have been running (unless it’s short enough thread execution body, instant is executed over), thread need to be interrupted in the operation process, the purpose is to make for other threads to perform, the details of thread scheduling strategy adopted by the depending on the underlying platform. Thread states can be blocked, ready, or dead. Such as:

  1. For the system using preemptive strategy, the system will allocate a time slice to each executable thread to process the task; When the time slice runs out, the system deprives the thread of its resources and gives other threads the opportunity to execute. The thread goes from running to ready again, waiting for the system to allocate resources.
  2. For a system with a cooperative strategy, it is only when a thread calls its yield() method that it relinquish the occupied resource — that is, it must voluntarily relinquish the occupied resource, and the thread will go from running to ready again.

4 The virtual drive is Blocked

A running thread, under certain circumstances, relinquishes the CPU and temporarily stops itself, entering a blocked state.

A thread will block when:

  1. The thread calls the sleep() method to give up the occupied processor resources and enter the interrupt state temporarily (the object lock held will not be released). When the time is up, it waits for the system to allocate CPU to continue execution.
  2. The thread calls a blocking IO method and blocks until the method returns.
  3. A thread tries to acquire a synchronization monitor, but the synchronization monitor is being held by another thread;
  4. The program calls the suspend method of the thread to suspend it.
  5. The thread calls wait and waits for notify/notifyAll to wake up.

Classification of blocking state:

  1. Wait to block: a running thread executes wait() to make it wait to block.
  2. Synchronized blocking: when a thread fails to acquire a synchronized lock (because the lock is occupied by another thread), it enters the synchronized blocking state.
  3. Other blocking: A thread enters a blocking state by calling its sleep() or join() or by making an I/O request. When the sleep() state times out, when the join() wait thread terminates or times out, or when the I/O processing is complete, the thread returns to the ready state.

A blocked thread can only enter the ready state, not the running state directly. The transition between ready and running state is usually determined by system thread scheduling rather than program control. When a thread in the ready state acquires a processor resource, the thread enters the running state. When a running thread loses processor resources, the thread enters the ready state.

With one exception, the yield() method is invoked to put a running thread into a ready state.

4.1 WAITING Status

A thread is in an unrestricted wait state, waiting for a special event to wake it up, such as:

  1. Threads waiting with wait() wait for a notify() or notifyAll() method;
  2. A thread waiting via join() wakes up when the target thread finishes running.

In both cases, once a thread is awakened by an event, it enters a RUNNABLE state and continues running.

4.2 TIMED_WAITING Status

The thread enters a time-bound wait state, such as:

Sleep (3000), and wait 3 seconds for the thread to resume running in RUNNABLE state.

5 Dead state

A thread ends in one of three ways, and then dies:

  1. The run() or call() method completes, and the thread terminates normally;
  2. The thread throws an uncaught Exception or Error;
  3. Call the thread stop() method directly to terminate the thread — this method is prone to deadlocks and is generally not recommended;

A thread object in a dead state may be alive, but it is no longer a thread executing alone. Once a thread dies, it cannot be resurrected. If on a dead thread calls the start () method, which will be thrown. Java lang. IllegalThreadStateException anomalies.

So, here are some things to note:

Once a thread is started using the start() method, it can never return to the NEW state, nor can it return to the RUNNABLE state after it terminates.

5.1 TERMINATED state

The thread TERMINATED after execution.

6 Thread-related methods

public class Thread{
    // Start the thread
    public void start(a); 
    / / thread
    public void run(a); 
    / / has been abandoned
    public void stop(a); 
    / / has been abandoned
    public void resume(a); 
    / / has been abandoned
    public void suspend(a); 
    // Sleep the currently executing thread for the specified number of milliseconds
    public static void sleep(long millis); 
    // Add the nanosecond parameter
    public static void sleep(long millis, int nanos); 
    // Tests whether the thread is active
    public boolean isAlive(a); 
    // Interrupt the thread
    public void interrupt(a); 
    // Test whether the thread has been interrupted
    public boolean isInterrupted(a); 
    // Tests whether the current thread has been interrupted
    public static boolean interrupted(a); 
    // Wait for the thread to terminate
    public void join(a) throws InterruptedException; 
    // The maximum time to wait for this thread to terminate is millis milliseconds
    public void join(long millis) throws InterruptedException; 
    // The maximum time to wait for this thread to terminate is millis milliseconds + nanos nanoseconds
    public void join(long millis, int nanos) throws InterruptedException; 
}
Copy the code

6.1 Thread ready, running, and dead state transitions

  1. Transition from ready state to running state: this thread gets CPU resources;
  2. Running state transitions to ready state: This thread actively calls yield() or loses CPU resources while running.
  3. The running state changes to dead: the thread completes execution or an exception occurs;

Note:

When the yield() method in A thread is called, the thread transitions from the running state to the ready state, but the CPU then schedules the thread in the ready state with some randomness, so it is possible for the CPU to schedule thread A even after the yield() method is called.

6.2 run & start

The thread is started by calling start, and the code in the run method is executed when the thread executes.

  1. Start () : the start of the thread;
  2. Run () : the execution body of the thread;

6.3 sleep & yield

Sleep () : Puts the thread to sleep for a period of time by sleep(millis), which cannot be woken up for a specified period of time and does not release the object lock;

For example, we want to sleep the main thread every 100 milliseconds before printing the number:

/** * You can see that the printed numbers are slightly spaced */
public class Test1 {  
    public static void main(String[] args) throws InterruptedException {  
        for(int i=0; i<100; i++){ System.out.println("main"+i);  
            Thread.sleep(100); }}}Copy the code

Note the following points:

  1. Sleep is a static method, and it is best not to call it with an instance object of Thread.Because it always sleeps with the currently running thread, not the thread object calling it.It is valid only for thread objects in the running state. Look at the following example:
    public class Test1 {  
        public static void main(String[] args) throws InterruptedException {  
            System.out.println(Thread.currentThread().getName());  
            MyThread myThread=new MyThread();  
            myThread.start();  
            // Sleep is the main thread, not myThread
            myThread.sleep(1000); 
            Thread.sleep(10);  
            for(int i=0; i<100; i++){ System.out.println("main"+i); }}}Copy the code
  2. Java thread scheduling is the core of Java multithreading. Only good scheduling can give full play to the performance of the system and improve the execution efficiency of the program. However, no matter how programmers write schedules, they can only affect the order of thread execution to the maximum extent, but cannot achieve precise control.After using the sleep method, the thread will enter the blocked state, and only when the sleep time is over, it will re-enter the ready state, and the ready state will enter the running state, which is controlled by the system, so it is impossible for us to accurately interfere with itSo if thread.sleep (1000) is called to make the Thread sleep for 1 second, the result may be greater than 1 second.
    public class Test1 {  
        public static void main(String[] args) throws InterruptedException {  
            new MyThread().start();  
            newMyThread().start(); }}class MyThread extends Thread {  
        @Override  
        public void run(a) {  
            for (int i = 0; i < 3; i++) {  
                System.out.println(this.getName()+"Thread" + i + "Second execution!");  
                try {  
                    Thread.sleep(50);  
                } catch(InterruptedException e) { e.printStackTrace(); }}}}Copy the code

    Look at the results of a particular runThread 0 executes first, thread 1 executes once, and thread 1 executes again. The discovery is not performed in the order of sleep.

    Thread-0 Thread 0 times! Thread-1 Thread 0 times executed! Thread-1 Thread executes once! Thread-0 Thread 1 execution! Thread-0 Thread executes twice! Thread-1 Thread executes twice!Copy the code

Yield () : Similar to sleep, Thread provides a static method that pauses the currently executing Thread, freeing CPU resources for another Thread. Unlike the sleep() method, however, it does not go into the blocking state, but into the ready state. The yield() method simply pauses the current thread and re-enters the ready thread pool, allowing the system’s thread scheduler to re-schedule it. It is entirely possible that when a thread calls yield(), the thread scheduler will re-schedule it and return it to the running state.

In fact, when a thread suspends with yield(), ready threads with the same or higher priority than the current thread are more likely to get the chance to execute. Just maybe, of course, because we can’t precisely interfere with the CPU scheduling threads.

public class Test1 {  
    public static void main(String[] args) throws InterruptedException {  
        new MyThread("Junior".1).start();  
        new MyThread("Intermediate".5).start();  
        new MyThread("Senior".10).start(); }}class MyThread extends Thread {  
    public MyThread(String name, int pro) {  
        super(name);// Set the thread name
        this.setPriority(pro);// Set the priority
    }  
  
    @Override  
    public void run(a) {  
        for (int i = 0; i < 30; i++) {  
            System.out.println(this.getName() + "Thread control" + i + "Second execution!");  
            if (i % 5= =0) Thread.yield(); }}}Copy the code

Here are the differences between the sleep() method and the yield() method:

  1. After the sleep method suspends the current thread, it blocks and only goes to the ready state when it is time to sleep. When the yield method is called, it goes directly to the ready state, so it is possible to get into the ready state and then be scheduled to the run state.
  2. The sleep method declaration throws InterruptedException, so catch the exception when calling the sleep method or display the declaration to throw it. The yield method does not declare to throw a task exception;
  3. The sleep method is more portable than the yield method, and generally should not be relied on to control the execution of concurrent threads;

6.4 the join

Thread merging means combining threads from several parallel threads into a single Thread. When a Thread must wait for another Thread to complete its execution, the Thread class provides a join method to do this. Note that this method is not static.

Join has three overloaded methods:

Void join() The current thread waits for the thread to terminate. Void Join (long millis) The maximum time that the current thread waits for this thread to terminate is millis milliseconds. Void JOIN (long Millis,int Nanos) Waits for the thread to terminate in millis milliseconds + nanos nanoseconds. If the thread does not complete within millis time, the current thread enters the ready state and waits for the CPU to schedule againCopy the code

Example code, as follows:

/** * Call thread.join() on the main thread; The main thread is appended to the thread child thread for execution. However, there is a time limit of 1 millisecond. * /
public class Test1 {  
    public static void main(String[] args) throws InterruptedException {  
        MyThread t=new MyThread();  
        t.start();  
        t.join(1);// Add the main thread to the child thread, but if the child thread does not complete within 1 millisecond, the main thread does not wait for it to complete and enters the ready state, waiting for the CPU to schedule it
        for(int i=0; i<30; i++){ System.out.println(Thread.currentThread().getName() +"Thread control" + i + "Second execution!"); }}}class MyThread extends Thread {  
    @Override  
    public void run(a) {  
        for (int i = 0; i < 1000; i++) {  
            System.out.println(this.getName() + "Thread control" + i + "Second execution!"); }}}Copy the code

Join method in JDK source, as follows:

public final synchronized void join(long millis)    throws InterruptedException {  
    long base = System.currentTimeMillis();  
    long now = 0;  
  
    if (millis < 0) {  
        throw new IllegalArgumentException("timeout value is negative");  
    }  
          
    if (millis == 0) {  
        while (isAlive()) {  
           wait(0); }}else {  
        while (isAlive()) {  
            long delay = millis - now;  
            if (delay <= 0) {  
                break; } wait(delay); now = System.currentTimeMillis() - base; }}}Copy the code

The Join method is implemented by calling the wait method. When the main thread calls t. jin, the main thread acquires the lock on t and calls its wait until the object wakes up, for example after exiting. This means that when the main thread calls t.jin, it must be able to get the lock on the thread t object.

6.5 suspend & Resume

Suspend – The thread enters a blocking state but does not release the lock. This method is no longer recommended because locks are not released during synchronization, causing deadlock problems.

Resume – Puts the thread back into an executable state.

Why are Thread.suspend and Thread.resume obsolete?

Thread.suspend naturally causes deadlocks. If the target thread holds a lock on a monitor protecting a system-critical resource when it hangs, other threads cannot access the resource until the target thread recovers. If the thread that is restoring the target thread attempts to lock the monitor before calling resume, a deadlock occurs. This deadlock usually manifests itself as a “frozen” process.

Other relevant information:

  1. https://blog.csdn.net/dlite/article/details/4212915

6.6 Stop (Obsolete)

It is not recommended and may be removed later as it is not safe. Why was Thread.stop scrapped?

Because it’s inherently unsafe. Stopping a thread causes it to unlock all the monitors that are locked on it (the monitors are unlocked by raising ThreadDeath exceptions at the top of the stack). If any objects previously protected by these monitors are in an inconsistent state, those objects seen by other threads will be in an inconsistent state. Such an object is called a damaged. When a thread operates on a corrupted object, it causes arbitrary behavior. This behavior can be subtle and difficult to detect, or it can be obvious.

Unlike other unchecked (unchecked) exceptions, ThreadDeath silently kills and other threads. As a result, the user is not warned that the application may crash. The collapse can manifest itself at any time after the actual damage has occurred, even hours or days later.

Other relevant information:

  1. https://blog.csdn.net/dlite/article/details/4212915

6.7 wait & notify/notifyAll

Wait, notify, and notifyAll are methods of the Object class. To use wait, notify, and notifyAll, the lock on the calling object is obtained first.

  1. After the wait method is called, the object lock is released, the thread status changes from Running to Waiting, and the current thread is placed in the object wait queue.
  2. After notifyAll or notify is called, the wait thread does not return from wait. The wait thread has to release the lock after noitfy is called.
  3. Notify method: one wait thread of the wait queue is moved from the wait queue to the synchronization queue, and notifyAll method: all wait threads of the wait queue are moved to the synchronization queue, and the status of the moved thread changes from Waiting to Blocked.

The two concepts mentioned above, wait queue (wait pool) and synchronization queue (lock pool), are not the same. Details are as follows:

Synchronization queue (lock pool) : Suppose thread A already owns the lock on an object (not A class), and other threads want to call A synchronized method (or block) of that object. Since these threads must acquire ownership of the lock before they can access the synchronized method of the object, However, the lock on the object is currently owned by thread A, so these threads are in the synchronized queue (lock pool) of the object, and their state is Blocked.

Wait queue (wait pool) : If thread A calls wait() on an object, thread A releases the lock on that object (since wait() must occur in synchronized, thread A naturally owns the lock on that object before executing its wait()). At the same time, thread A enters the Waiting queue (Waiting pool) of the object, and the state of thread A is Waiting. If another thread calls notifyAll() on the same object, all the threads in the wait pool of the object are put into the synchronization queue (lock pool) of the object, ready to claim ownership of the lock. If another thread calls notify() on the same object, only one thread (at random) in the wait pool of that object will enter the synchronization queue (lock pool) of that object.

Threads that are aroused by notify or notifyAll have the following regularity:

  1. If the thread is invoked by notify, the thread that entered wait first will be invoked first.
  2. If the thread is invoked by nootifyAll, the default is that the last thread to enter is invoked first, which is LIFO’s policy.

6.8 Thread Priority

Each thread has a priority attribute when it executes, and threads with higher priority get more execution opportunities, while threads with lower priority get less execution opportunities. Like thread sleep, thread priority still does not guarantee thread execution order. However, threads with higher priority are more likely to acquire CPU resources, and threads with lower priority are not deprived of execution.

By default, each thread has the same priority as the parent thread that created it. By default, the main thread has normal priority;

The Thread class provides setPriority(int newPriority) and getPriority() methods to set and return the priority of a specified Thread. The setPriority method takes an integer between 1 and 10. You can also use the three static constants provided by the Thread class:

MAX_PRIORITY   =10
MIN_PRIORITY   =1
NORM_PRIORITY   =5
Copy the code

Example code, as follows:

public class Test1 {  
    public static void main(String[] args) throws InterruptedException {  
        new MyThread("Senior".10).start();  
        new MyThread("Junior".1).start(); }}class MyThread extends Thread {  
    public MyThread(String name,int pro) {  
        super(name);// Set the thread name
        setPriority(pro);// Set the priority of the thread
    }  
    @Override  
    public void run(a) {  
        for (int i = 0; i < 100; i++) {  
            System.out.println(this.getName() + "Thread control" + i + "Second execution!"); }}}Copy the code

As you can see from the execution results, in general, advanced threads are more likely to complete their execution.

Note one thing:

Although Java provides 10 priority levels, these priority levels require operating system support. Different operating systems have different priorities and do not correspond well to Java’s 10 priorities. The three static constants MAX_PRIORITY, MIN_PRIORITY, and NORM_PRIORITY should be used to set priorities for maximum portability.

6.9 Daemon Thread

Daemons are written almost the same as normal threads, and can be set to daemons by calling the thread object method setDaemon(true).

Daemons are used less often, but not useless; for example, JVM threads for garbage collection, memory management, and so on are daemons. There is also in the database application, the use of the database connection pool, connection pool itself also contains a lot of background threads, monitoring the number of connections, timeout, status and so on.

SetDaemon ();

Public final void setDaemon(Boolean on) : marks this thread as a daemon or user thread. The Java virtual machine exits when all running threads are daemons.

This method must be called before starting the thread. The method first calls the thread’s checkAccess method, taking no arguments. This may throw a SecurityException (in the current thread).

Parameters:

On - If true, the thread is marked as a daemon thread.Copy the code

Throws:

IllegalThreadStateException - if this thread is active. SecurityException - If the current thread cannot modify the thread.Copy the code
/** * Java threads: thread scheduling - daemon threads */  
public class Test {  
        public static void main(String[] args) {  
                Thread t1 = new MyCommon();  
                Thread t2 = new Thread(new MyDaemon());  
                t2.setDaemon(true);        // Set to daemon threadt2.start(); t1.start(); }}class MyCommon extends Thread {  
        public void run(a) {  
                for (int i = 0; i < 5; i++) {  
                        System.out.println("Thread 1" + i + "Second execution!");  
                        try {  
                                Thread.sleep(7);  
                        } catch(InterruptedException e) { e.printStackTrace(); }}}}class MyDaemon implements Runnable {  
        public void run(a) {  
                for (long i = 0; i < 9999999L; i++) {  
                        System.out.println("Background thread control" + i + "Second execution!");  
                        try {  
                                Thread.sleep(7);  
                        } catch(InterruptedException e) { e.printStackTrace(); }}}}Copy the code

Execution Result:

Background thread executes for the 0th time! Thread 1 executes 0th time! Thread 1 executes for the first time! Background thread executes first time! Background thread executes second time! Thread 1 executes for the second time! Thread 1 executes for the third time! Background thread executes for the third time! Thread 1 executes for the fourth time! The background thread executes for the fourth time! The background thread executes for the fifth time! The background thread executes for the sixth time! The background thread executes for the seventh time!Copy the code

As can be seen from the above execution result: the foreground thread is guaranteed to complete execution, while the background thread exits before completing execution.

In fact, the JRE’s criterion for determining whether a program has finished execution is that all foreground threads have finished regardless of the status of the background thread, so be aware of this problem when using background threads.

6.10 How Do I End a Thread

Thread. Stop (), Thread. Suspend, Thread, resume, Runtime. RunFinalizersOnExit these terminated threads running method has been abandoned, use them is extremely unsafe! To safely and efficiently terminate a thread, use the following methods.

  1. The run method completes normally, and then terminates;
  2. Control the loop condition and identifier of the judgment condition to terminate the thread;

For example, the run method is written like this: Just make sure that under certain circumstances, the run method can complete. Instead of the infinite loop of while(true).

class MyThread extends Thread {  
    int i=0;  
    @Override  
    public void run(a) {  
        while (true) {  
            if(i==10)  
                break; i++; System.out.println(i); }}} orclass MyThread extends Thread {  
    int i=0;  
    boolean next=true;  
    @Override  
    public void run(a) {  
        while (next) {  
            if(i==10)  
                next=false; i++; System.out.println(i); }}} orclass MyThread extends Thread {  
    int i=0;  
    @Override  
    public void run(a) {  
        while (true) {  
            if(i==10)  
                return; i++; System.out.println(i); }}}Copy the code

Admittedly, using the above method identifier to the end of a thread that is a good method, but it also has disadvantages, if the thread is in a state of sleep, wait, join, the while loop will not perform, then our identifier is useless, of course, also can’t through it to end in a state of the three threads.

So, in this case, interrupt is a clever way to terminate the thread. Let’s look at the declarations of the sleep, wait, and join methods:

public final void wait(a) throws InterruptedException 
public static native void sleep(long millis) throws InterruptedException
public final void join(a) throws InterruptedException
Copy the code

As you can see, all three have one thing in common: they throw an InterruptedException. When does such an exception occur?

Each Thread has an interrupt state, which defaults to false. The interrupted status of a Thread can be determined by using the isInterrupted() method of the Thread object. The interrupt state can be set to true through the Interrupt () method of the Thread object.

When a thread is in one of its sleep, Wait, or Join states and its interrupted status is true, it throws an InterruptedException and changes its interrupted status to False.

Here’s a simple example:

public class Test1 {  
    public static void main(String[] args) throws InterruptedException {  
        MyThread thread=newMyThread(); thread.start(); }}class MyThread extends Thread {  
    int i=1;  
    @Override  
    public void run(a) {  
        while (true) {  
            System.out.println(i);  
            System.out.println(this.isInterrupted());  
            try {  
                System.out.println("I'm going to sleep right now.");  
                Thread.sleep(2000);  
                this.interrupt();  
            } catch (InterruptedException e) {  
                System.out.println("Exception caught"+this.isInterrupted());  
                return; } i++; }}}Copy the code

Test results:

1  
falseI immediately went to sleep 2trueI immediately went to sleep and caught the exceptionfalse 
Copy the code

As you can see, the first while loop is executed. During the first loop, sleep for 2 seconds, and then set the interrupt state to true. When it enters the second loop, the interrupt status is set to true the first time, and when it enters sleep again, InterruptedException is thrown and caught. The interrupt state is then automatically set to false again (as you can see from the last output).

So, we can terminate a thread with the interrupt method. Specific use is as follows:

public class Test1 {  
    public static void main(String[] args) throws InterruptedException {  
        MyThread thread=new MyThread();  
        thread.start();  
        Thread.sleep(3000); thread.interrupt(); }}class MyThread extends Thread {  
    int i=0;  
    @Override  
    public void run(a) {  
        while (true) {  
            System.out.println(i);  
            try {  
                Thread.sleep(1000);  
            } catch (InterruptedException e) {  
                System.out.println("Interrupt exception caught");  
                return; } i++; }}}Copy the code

A few more times, you will find that there are generally two execution results:

The 0, 1, 2 interrupt exception was caughtCopy the code

or

The 0, 1, 2, 3 interrupt exception was caughtCopy the code

InterruptedException is thrown whenever a thread’s interrupted state is true, as soon as it is in a state such as sleep or sleep.

The first is when the main thread wakes up from its 3-second sleep and calls InterruptedException while the child thread is asleep.

The second case is when the main thread wakes up from the 3-second sleep state and calls the interrupt method on the child thread before it is in the sleep state. On the third while loop, it enters the sleep state and throws InterruptedException.