Thread life cycle, Thread class construction method and common API, as well as Thread closing method.

1 Thread life cycle

1.1 Five Stages

The thread lifecycle can be divided into five phases:

  • NEW
  • RUNNABLE
  • RUNNING
  • BLOCKED
  • TERMINATED

1.2 NEW

When a Thread object is created with new, but the Thread is not started with start(), the Thread is in the new state. To be precise, it’s just the state of the Thread object, which is a normal Java object. You can use the start() method to enter the RUNNABLE state.

1.3 a RUNNABLE

To enter the RUNNABLE state, you must call the start() method, which creates a thread in the JVM. However, once a thread is created, it cannot be executed immediately. Whether a thread executes or not depends on CPU scheduling. In other words, it is in an executable state and qualified for execution, but it is waiting to be scheduled instead of actually executing.

The RUNNABLE state can only end unexpectedly or enter the RUNNING state.

1.4 RUNNING

Once the CPU has selected a thread from the task executable queue by polling or other means, the thread can only be executed, that is, in the RUNNING state. In this state, the following state transitions may occur:

  • Enter TERMINATED: such as calling the stop() method, which is no longer recommended

  • BLOCKED: for example, sleep()/wait() methods are called, or a blocking action is performed (obtaining lock resources, disk I/O, etc.)

  • Enter RUNNABLE: the CPU time slice is up, or the thread actively calls yield()

1.5 BLOCKED

This is the blocking state. There are many reasons for entering the blocking state, including the following:

  • Disk I/o

  • Network operating

  • To enter a blocking operation in order to acquire a lock

  • When BLOCKED, the following state transitions can occur:

  • Enter TERMINATED: such as calling stop(), which is not recommended, or the JVM dies unexpectedly

  • Enter RUNNABLE: sleep ends, wake up by notify()/nofityAll(), acquire a lock, interrupt() interrupts blocking, etc

1.6 TERMINATED

TERMINATED is the final state of a thread. Entry into TERMINATED state indicates the end of the thread’s life cycle, for example, in the following cases:

  • The thread ends properly

  • A thread running error ended unexpectedly

  • The JVM crashes unexpectedly, forcing all threads to terminate

2 Thread constructor

2.1 Construction method

There are altogether eight constructors of Thread, which are classified according to their naming methods. The constructors using the default name are as follows:

  • Thread()

  • Thread(Runnable target)

  • Thread(ThreadGroup group,Runnable target)

A named thread is constructed as follows:

  • Thread(String name)

  • Thread(Runnable target,Strintg name)

  • Thread(ThreadGroup group,String name)

  • Thread(ThreadGroup group,Runnable target,String name)

  • Thread(ThreadGroup group,Runnable target,String name,long stackSize)

But in fact all constructors end up calling the following private constructor:

private Thread(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals);

In the default naming constructor, you can see in the source code that the default naming is actually the command for Thread-x (X is a number) :

public Thread() {

    this((ThreadGroup)null, (Runnable)null, "Thread-" + nextThreadNum(), 0L);

}

public Thread(Runnable target) {

    this((ThreadGroup)null, target, "Thread-" + nextThreadNum(), 0L);

}

private static synchronized int nextThreadNum() {

    return threadInitNumber++;

}
Copy the code

The name constructor is a custom name.

Also, if you want to change the name of a thread, you can call setName(), but note that only threads in the NEW state can change the name.

2.2 Parent-child Relationship of threads

All constructors of Thread call the following methods:

private Thread(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals);

Here’s a snippet of the source code:

if (name == null) { throw new NullPointerException("name cannot be null"); } else { this.name = name; Thread parent = currentThread(); SecurityManager security = System.getSecurityManager(); if (g == null) { if (security ! = null) { g = security.getThreadGroup(); } if (g == null) { g = parent.getThreadGroup(); }}}Copy the code

You can see that there is currently a local variable called parent and it’s assigned to currentThread(), which is a native method. Since the initial state of a thread is NEW, currentThread() represents the thread that created its own thread, i.e., the following conclusion:

The creation of one thread must be done by another thread

The parent of the created thread is the thread that created it

The parent thread is the main thread, which is created by the JVM.

In addition, several of the Thread constructors have the ThreadGroup parameter, which specifies which ThreadGroup the Thread is in. If a Thread is created without a specified ThreadGroup, it will have the same ThreadGroup as the parent Thread. The ThreadGroup of the main thread is called main.

2.3 about stackSize

The Thread constructor has a stackSize parameter that specifies the number of bytes in the address space that the JVM allocates to the Thread stack. It is platform dependent, on some platforms:

  • Setting a large value increases the recursion depth of in-thread calls and reduces the probability of StackOverflowErrors

  • Low value: Increases the number of threads created and delays outofMemoryErrors

However, on some platforms this parameter does not work at all. Also, setting it to 0 won’t do anything.

3 Thread API

3.1 the sleep ()

Sleep () has two overloaded methods:

  • sleep(long mills)

  • sleep(long mills,int nanos)

The sleep() method is wrapped in timeUnit.xxxx.sleep () instead of thread.sleep () :

TimeUnit.SECONDS.sleep(1);

TimeUnit.MINUTES.sleep(3);

3.2 yield ()

Yield () is a heuristic that alerts the CPU scheduler to the voluntary abandonment of resources by the current thread and will be ignored if CPU resources are not strained. Calling yield() causes the current thread to change from RUNNING to RUNNABLE.

The difference between yield() and sleep() is as follows:

  • Sleep () causes the current thread to pause for a specified amount of time, with no CPU time slice consumed

  • Yield () is simply a hint to the CPU scheduler that, if not ignored, will result in a thread context switch

  • Sleep () blocks the thread briefly, freeing CPU resources for a given amount of time

  • If yield() takes effect, yield() will make the RUNNING state go to RUNNABLE state

  • Sleep () will do almost 100% of the sleep for a given amount of time, but yield() hints are not necessarily guaranteed

  • One thread calling sleep() and another calling interrupt() catches an interrupt signal, while yield does not

3.3 setPriority ()

3.3.1 Priorities

Threads, like processes, have their own priorities. In theory, a thread with a higher priority has a chance to be scheduled first, but in practice this is not the case. Setting a priority is similar to yield() and is also a reminder operation:

For the root user, the operating system is reminded of the priority that you want to set, otherwise it will be ignored

If the CPU is busy, setting the priority may get more CPU time slices, but when the CPU is idle, the priority level has little effect

Therefore, setting a priority is to a large extent to allow a thread to get as many execution opportunities as possible, that is, to allow the thread itself to be scheduled by the operating system as much as possible, rather than setting a higher priority must run first, or a higher priority thread must run first than a lower priority thread.

3.3.2 Priority source code analysis

To set the priority, call setPriority().

public final void setPriority(int newPriority) {

    this.checkAccess();

    if (newPriority <= 10 && newPriority >= 1) {

        ThreadGroup g;

        if ((g = this.getThreadGroup()) != null) {

            if (newPriority > g.getMaxPriority()) {

                newPriority = g.getMaxPriority();

            }

            this.setPriority0(this.priority = newPriority);

        }

    } else {

        throw new IllegalArgumentException();

    }

}
Copy the code

It can be seen that the priority is between [1,10] and cannot be set higher than the priority of the current ThreadGroup. Finally, set the priority through the native method setPriority0.

Normally, the priority level of a thread is not set. By default, the priority of a thread is 5, because the main thread has a priority of 5 and main is the parent of all threads, so it also has a priority of 5 by default.

3.4 interrupt ()

Interrupt () is an important API. There are three apis for thread interrupts:

  • void interrupt()
  • boolean isInterrupted()
  • static boolean interrupted()

The following will analyze them one by one.

3.4.1 track interrupt ()

Some method calls cause the current thread to block, such as:

  • Object.wait()

  • Thread.sleep()

  • Thread.join()

  • Selector.wakeup()

Calling interrupt(), however, interrupts a block. Interrupting a block does not end the thread’s life cycle, but merely interrupts the blocking state of the current thread. Once interrupted while blocking, an InterruptedException is thrown, which acts as a signal to inform the current thread that it has been interrupted, as shown in the following example:

public static void main(String[] args) throws InterruptedException{ Thread thread = new Thread(()->{ try{ TimeUnit.SECONDS.sleep(10); }catch (InterruptedException e){ System.out.println("Thread is interrupted."); }}); thread.start(); TimeUnit.SECONDS.sleep(1); thread.interrupt(); }Copy the code

Outputs a message that the thread was interrupted.

3.4.2 isInterrupted ()

IsInterrupted () checks whether the current thread has been interrupted. This checks only for interrupt() and does not affect any changes to the interrupt() flag, as shown in the following example:

public static void main(String[] args) throws InterruptedException{

    Thread thread = new Thread(()->{

        while (true){}

    });

    thread.start();

    TimeUnit.SECONDS.sleep(1);

    System.out.println("Thread is interrupted :"+thread.isInterrupted());

    thread.interrupt();

    System.out.println("Thread is interrupted :"+thread.isInterrupted());

}
Copy the code

The output is:

Thread is interrupted :false

Thread is interrupted :true
Copy the code

Here’s another example:

public static void main(String[] args) throws InterruptedException { Thread thread = new Thread() { @Override public void run() { while (true) { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { System.out.println("In catch block thread is interrupted :" + isInterrupted()); }}}}; thread.start(); TimeUnit.SECONDS.sleep(1); System.out.println("Thread is interrupted :" + thread.isInterrupted()); thread.interrupt(); TimeUnit.SECONDS.sleep(1); System.out.println("Thread is interrupted :" + thread.isInterrupted()); }Copy the code

Output result:

Thread is interrupted :false

In catch block thread is interrupted :false

Thread is interrupted :false
Copy the code

If the Thread is not interrupted at first, the result is false. If the interrupt method is called and an exception is caught in the loop, the Thread itself will erase the interrupt identifier and reset it, so the output is false.

Rule 3.4.3 interrupted ()

This is a static method. Calling this method erases the interrupt mark on the thread. Note that if the current thread is interrupted:

  • The first call to interrupted() returns true and immediately erases the interrupt flag

  • The second and subsequent calls always return false, unless the thread is interrupted again in the meantime

Examples are as follows:

public static void main(String[] args) throws InterruptedException { Thread thread = new Thread() { @Override public void run() { while (true) { System.out.println(Thread.interrupted()); }}}; thread.setDaemon(true); thread.start(); TimeUnit.MILLISECONDS.sleep(2); thread.interrupt(); }Copy the code

Output (partial cut) :

false

false

false

true

false

false

false

Copy the code

If interrupted() is interrupted, the interrupt flag is immediately erased and true is returned for this only case.

The difference between interrupted() and isInterrupted() can be seen in the source code (OpenJDK 11) :

public static boolean interrupted() {

    return currentThread().isInterrupted(true);

}

public boolean isInterrupted() {

    return this.isInterrupted(false);

}

@HotSpotIntrinsicCandidate

private native boolean isInterrupted(boolean var1);
Copy the code

In fact, both call the same native method, where the Boolean variable indicates whether the thread’s interrupt identifier is erased:

  • True means you want to erase, which is what interrupted() does

  • False means you don’t want to erase, isInterrupted() does that

3.5 the join ()

3.5.1 track of the join ()

Join (), like sleep(), is a method that can interrupt. If other threads interrupt the current thread, they will catch the interrupt signal and erase the thread’s interrupt identifier. Join () provides three apis, as follows:

  • void join()
  • void join(long millis,int nanos)
  • void join(long mills)

3.5.2 example

A simple example is as follows:

public class Main { public static void main(String[] args) throws InterruptedException { List<Thread> threads = IntStream. Range (1, 3). MapToObj (Main: : create). Collect (Collectors. ToList ()); threads.forEach(Thread::start); for (Thread thread:threads){ thread.join(); } for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+" # "+i); shortSleep(); } } private static Thread create(int seq){ return new Thread(()->{ for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName()+" # "+i); shortSleep(); } },String.valueOf(seq)); } private static void shortSleep(){ try{ TimeUnit.MILLISECONDS.sleep(2); }catch (InterruptedException e){ e.printStackTrace(); }}}Copy the code

The output is captured as follows:

2 # 8

1 # 8

2 # 9

1 # 9

main # 0

main # 1

main # 2

main # 3

main # 4
Copy the code

Thread 1 and thread 2 execute alternately, while the main thread waits until thread 1 and thread 2 have finished executing.

4 Thread Shutdown

Thread has an outdated stop method that can be used to close a Thread, but the problem is that the monitor lock may not be released, so it is not recommended to close a Thread using this method. Thread closure can be divided into three categories:

  • Normally closed

  • Abnormal exit

  • Feign death

4.1 Normal Shutdown

4.1.1 Normal End

When a thread finishes running, it exits normally, which is the most common case.

4.1.2 Capturing a signal to close the thread

To close a thread by catching an interrupt signal, as shown in the following example:

public static void main(String[] args) throws InterruptedException { Thread t = new Thread(){ @Override public void run() { System.out.println("work..." ); while(! isInterrupted()){ } System.out.println("exit..." ); }}; t.start(); TimeUnit.SECONDS.sleep(5); System.out.println("System will be shutdown."); t.interrupt(); }Copy the code

Always check to see if the interrupt flag is set to true, which breaks the loop. Another way is to use sleep() :

public static void main(String[] args) throws InterruptedException { Thread t = new Thread(){ @Override public void run() { System.out.println("work..." ); while(true){ try{ TimeUnit.MILLISECONDS.sleep(1); }catch (InterruptedException e){ break; } } System.out.println("exit..." ); }}; t.start(); TimeUnit.SECONDS.sleep(5); System.out.println("System will be shutdown."); t.interrupt(); }Copy the code

4.1.3 volatile

Since the interrupt identifier is likely to be erased or the interrupt() method will not be called, the alternative is to use volatile to modify a Boolean variable and iterate:

public class Main { static class MyTask extends Thread{ private volatile boolean closed = false; @Override public void run() { System.out.println("work..." ); while (! closed && ! isInterrupted()){ } System.out.println("exit..." ); } public void close(){ this.closed = true; this.interrupt(); } } public static void main(String[] args) throws InterruptedException { MyTask t = new MyTask(); t.start(); TimeUnit.SECONDS.sleep(5); System.out.println("System will be shutdown."); t.close(); }}Copy the code

4.2 Abnormal Exit

Unchecked exceptions are not allowed to be thrown in thread execution units. If you need to catch checked exceptions while a thread is running and determine whether it is still necessary to run, encapsulate them as unchecked exceptions, such as RuntimeException. Throws to end the thread’s life cycle.

4.3 feign death

Suspended animation is a thread that exists but has no outward appearance, such as:

No log output

You don’t do any homework

Wait, even though the thread exists, it looks like it’s dead, but in fact it’s not. This is most likely because the thread is blocked, or two threads are competing for resources and there is a deadlock.

This situation requires external tools such as VisualVM, JConsole, etc., to identify the thread in question and its current state, and to determine which method is blocking.

  • That’s all about Thread.

  • Also welcome everybody exchange discussion, if this article has incorrect place, hope everybody many forgive.

  • Your support is my biggest motivation, if you help out, give me a thumbs up