1. Create a thread

First of all, let’s review the thread creation that we learned when we were learning Java, which is also a favorite question in the interview, some people say two kinds, some people say three kinds, four kinds, etc. Actually, we should not memorize, but we should understand the principle, and when we understand it, we will find that the so-called thread creation is essentially the same. In our interview process, if we can answer such questions from the essence, then believe must be a plus! So without further ado, let’s start today’s code journey

1.1 ** inherits the Thread class to create threads **

**

  • This is the most common way we create threads, by inheritanceThreadClass to rewriterunMethod,

The code is as follows:


/** * Thread class * URL: www.i-code.online *@author: anonyStar
 * @time: 2020/9/24 18:55 * /
public class ThreadDemo extends Thread {
    @Override
    public void run(a) {
        // Threads execute content
        while (true) {try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("ThredDemo thread executing, thread name:"+ Thread.currentThread().getName()); }}}Copy the code

Test method:

    @Test
    public void thread01(a){
        Thread thread = new ThreadDemo();
        thread.setName("Thread-1");
        thread.start();

        while (true){
            System.out.println("This is the main thread:" + Thread.currentThread().getName());
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch(InterruptedException e) { e.printStackTrace(); }}}Copy the code

Results:

Threads that inherit Thread are easy to create and start directly by calling the start method rather than the run method. Calling run directly is equivalent to calling a normal method, not starting the thread

1.2 ** Implements the Runnable interface to create threads **

**

  • The above way we are through inheritance to achieve, then injavaProvided in theRunnableInterface, we can directly implement that interface, implement therunMethod, which is more scalable

The code is as follows:


/**
 * url: www.i-code.online
 * @author: anonyStar
 * @time: 2020/9/24 18:55 * /
public class RunnableDemo implements Runnable {
 
    @Override
    public void run(a) {
        // Threads execute content
        while (true) {try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("RunnableDemo thread executing, thread name:"+ Thread.currentThread().getName()); }}}Copy the code

Test code:

    @Test
    public void runnableTest(a){
        // The new Thread class is passed in as the Runnable implementation class
        Thread thread = new Thread(new RunnableDemo(),"Runnable child thread - 1");
        // Start the thread
        thread.start();

        while (true){
            System.out.println("This is the main thread:" + Thread.currentThread().getName());
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch(InterruptedException e) { e.printStackTrace(); }}}Copy the code

Running results:



1.3 Implement the Callable interface to create threads


  • This way is through implementationCallableInterface to implement thecallMethod to implement threads, but the way this thread is created depends on ** ****FutureTask **The wrapperTo create aThreadLook at the code

The code is as follows:


/**
 * url: www.i-code.online
 * @author: anonyStar
 * @time: 2020/9/24 18:55 * /
public class CallableDemo implements Callable<String> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    @Override
    public String call(a) throws Exception {
        // Threads execute content
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("The CallableDemo thread is executing, thread name:"+ Thread.currentThread().getName());

        return "CallableDemo execution completed..."; }}Copy the code

Test code:

    @Test
    public void callable(a) throws ExecutionException, InterruptedException {
        // Create a thread pool
        ExecutorService service = Executors.newFixedThreadPool(1);
        // The Callable implementation starts the thread at the same time
        Future submit = service.submit(new CallableDemo());
        // Gets the return value of the thread contents for subsequent logic
        System.out.println(submit.get());
        // Close the thread pool
        service.shutdown();
        / / main thread
        System.out.println("This is the main thread:" + Thread.currentThread().getName());
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch(InterruptedException e) { e.printStackTrace(); }}Copy the code

Results:



In some cases, we may need to have the one-step thread supply a return value to the current main thread after completion, and the main thread needs to rely on this value for subsequent logical processing. In this case, we need to use the thread with the return value








2. Thread lifecycle

  • Since Java threads can be created, they must also be destroyed, so threads have a life cycle, so let’s start with the life cycle of threads to understand threads.

2.1 Thread Status

2.1.1 Recognition of six thread states

Threads are TERMINATED in six states (NEW, RUNNABLE, BLOCKED, WAITING, TIME_WAITING, and TERMINATED).

  • NEW: Initial state, the thread is built, but the start method has not been called

  • RUNNABLED: The running state. JAVA threads refer to the ready and running states of the operating system collectively as “running.”

  • BLOCKED: a thread enters a waiting state, where it has given up CPU usage for some reason

    • Preparation of a waiting lock for a running thread
    • Other blocking: When a running Thread executes thread. sleep or t.join methods, or makes an I/O request, the JVM sets the current Thread to block. When the sleep ends, the join Thread terminates, and the I/O process completes, the Thread resumes
  • TIME_WAITING: Timeout waiting state. After a timeout, the system automatically returns

  • TERMINATED: indicates that the current thread is TERMINATED


2.1.2 Code practical operation demonstration

  • Code:

    public static void main(String[] args) {
        ////TIME_WAITING Uses sleep wait (time) to enter the wait timeout
        new Thread(() -> {
           while (true) {// Threads execute content
               try {
                   TimeUnit.SECONDS.sleep(100);
               } catch(InterruptedException e) { e.printStackTrace(); }}},"Time_Waiting").start();
        //WAITING, the thread waits on the ThreadStatus lock
        new Thread(() -> {
            while (true) {synchronized (ThreadStatus.class){
                    try {
                        ThreadStatus.class.wait();
                    } catch(InterruptedException e) { e.printStackTrace(); }}}},"Thread_Waiting").start();

        //synchronized gains the lock, and the other enters the blocked state
        new Thread(() -> {
            while (true) {synchronized(Object.class){
                    try {
                        TimeUnit.SECONDS.sleep(100);
                    } catch(InterruptedException e) { e.printStackTrace(); }}}},"Object_blocked_1").start();
        new Thread(() -> {
            while (true) {synchronized(Object.class){
                    try {
                        TimeUnit.SECONDS.sleep(100);
                    } catch(InterruptedException e) { e.printStackTrace(); }}}},"Object_blocked_2").start();
    }
Copy the code

It is a good idea to set a thread name for a thread before starting it, because this will give the developer hints when using the JStack profiler or troubleshooting problems

2.1.3 Thread state stack


Preparation: To run this example, open a terminal or command prompt and typejps“,JDK1.5Provides a display of current alljava processpid The command)



Trained in accordance with the previous steppid, continue typing jstack pid(jstack isjava A stack trace tool that comes with a VIRTUAL machine. Jstack is used to print the givenjava processID core fileOr remote debug servicesJava Stack information)



3. In-depth parsing of threads

3.1 Starting Mechanism of threads

  • Earlier we demonstrated the start of a thread, that is, a call, with some examplesstart()Method to start a thread whenrunThe thread life cycle terminates after the code in the method completes execution. callstartMethod semantics are told by the current threadJVM, start the callstartMethod thread.
  • One of the big puzzles when we started learning about threads was how to start a threadstartMethod instead of calling directlyrunMethod. So let’s look at it briefly firststartMethod definition, inThreadIn the class
    public synchronized void start(a) {
        /** * This method is not invoked for the main method thread or "system" * group threads created/set up by the VM. Any new functionality added * to this method in the future may have to also be added to the VM. * * A zero status value corresponds to state "NEW". */
        if(threadStatus ! =0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started * so that it can be added to the group's list of threads * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            // Core method called by thread, this is a native method, native
            start0();
            started = true;
        } finally {
            try {
                if(! started) { group.threadStartFailed(this); }}catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then it will be passed up the call stack */}}}// The thread calls the native method
    private native void start0(a);
Copy the code
  • And we can see that herestartMethod is callednativeMethods * *start0** to start the thread, this method is inThreadClass, one of which is called directly herenativemethodsregisterNatives
    /* Make sure registerNatives is the first thing <clinit> does. */
    private static native void registerNatives(a);
    static {
        registerNatives();
    }
Copy the code
  • Since the registerNatives method is a local method, we must download JDK source code to see its implementation. For JDK and hotspot source code download, you can go to the openJDK official website to download, refer to:

  • We can view the local source or directly to hg.openjdk.java.net/jdk8u/jdk8u… Look at the Thread class corresponding to the local method.c file,

  • As shown above, we download it locallyjdkProject, find src->share->native->java->lang->Thread.cfile

  • The above isThread.cAll the code in, we can see the callRegisterNativesAnd at the same time you can seemethodA mapping in a collection that calls a local methodstart0Is actually calledJVM_StartThreadWhich itself is made up ofc/c++Realization, here need to view in the virtual machine source code, we are usinghostpotVirtual machine, this one can goopenJDKThe official website download, the above introduction will not say more
  • We seeJVM_StartThread The definition of is injvm.hSource code, andjvm.hThe implementation is in the virtual machinehotspotMedium, we openhotspotSource code, findsrc -> share -> vm -> prims ->jvm.cppFile,2955Row, can be retrieved directlyJVM_StartThread , the method code is as follows:

JVM_ENTRY(void.JVM_StartThread(JNIEnv* env, jobject jthread))
  JVMWrapper("JVM_StartThread");
  JavaThread *native_thread = NULL;

  bool throw_illegal_thread_state = false;

  {
    MutexLocker mu(Threads_lock);

    if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) ! =NULL) {
      throw_illegal_thread_state = true;
    } else {
      // We could also check the stillborn flag to see if this thread was already stopped, but
      // for historical reasons we let the thread detect that itself when it starts running
      // <1> : Gets the number of threads in the current process
      jlong size =
             java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));

      size_t sz = size > 0 ? (size_t) size : 0;

      // <2> : actually calls the method that creates the thread
      native_thread = new JavaThread(&thread_entry, sz);
      if (native_thread->osthread() != NULL) {
        // Note: the current thread is not being used within "prepare".
        native_thread->prepare(jthread); }}}if (throw_illegal_thread_state) {
    THROW(vmSymbols::java_lang_IllegalThreadStateException());
  }

  assert(native_thread ! =NULL."Starting null thread?");

  if (native_thread->osthread() = =NULL) {
    // No one should hold a reference to the 'native_thread'.
    delete native_thread;
    if (JvmtiExport::should_post_resource_exhausted()) {
      JvmtiExport::post_resource_exhausted(
        JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS,
        "unable to create new native thread");
    }
    THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(),
              "unable to create new native thread");
  }

  // <3> Start the thread
  Thread::start(native_thread);

JVM_END
Copy the code

JVM_ENTRY is used to define the JVM_StartThread function, which creates a real platform-specific local thread, marked <2> above

  • To further thread creation, let’s enternew JavaThread(&thread_entry, sz)To see the specific implementation process, inthread.cppfile1566The row is definednewThe method of

  • With the above code we can see the final callos::create_thread(this, thr_type, stack_sz);To implement thread creation, and there are different implementations of this method for different platforms, which I won’t go into here,

  • It’s all created and called laterThread::start(native_thread);Called in JVM_StartThread, the implementation of this method inThread.cpp

The start method has a function call: OS ::start_thread(thread); , calling the platform method that starts the Thread, which eventually calls the JavaThread:: Run () method in thread.cpp


3.2 Termination of a thread

3.2.1 Terminate threads by marking bits

  • Normally, things in our thread are executed in a loop, so we must also have a need to stop the current thread in another thread, this is later we can use the tag bit to achieve, the so-called tag is actuallyvolatileThe modified variable is determined by its visibility properties, as shown in the following codevolatileTo implement the tag bit to stop the thread

    // define the tag to use volatile modifier
    private static volatile  boolean mark = false;

    @Test
    public void markTest(a){
        new Thread(() -> {
            // Check the flag bit to determine whether to proceed
            while(! mark){try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Thread execution content...");
            }
        }).start();

        System.out.println("This is the main thread going...");
        try {
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // After 10 seconds will be marked to set true visible to the thread. With volatile
        mark = true;
        System.out.println("Change the flag bit to:"+mark);
    }
Copy the code

3.2.2 Terminating a thread by stopping

  • In summary:

    • callstop()The method will stop immediatelyrun()All remaining work in the method is included incatchfinallyAnd throwThreadDeathException (normally this exception does not require display capture), and therefore may result in some work not being completed, such as file, database, etc.
    • callstop()Method immediately releases all locks held by the thread, causing data to be unsynchronized and inconsistent.

3.2.3 Terminating a thread with interrupt

  • From the above elaboration, we know the use ofstopMethods are not recommended, so what is the best way to stop a threadinterruptMethod, which we do by callinginterruptTo interrupt the thread
  • When another thread calls the current thread’sinterrupt Method, indicating that the current thread is ready to interrupt its execution. When to interrupt is up to the current thread
  • The thread checks to see if it is interrupted and can passisInterrupted()To determine whether it is interrupted.

Let’s look at the following code:

    public static void main(String[] args) {
        // Create interrupt-1 threads

        Thread thread = new Thread(() -> {
            while (true) {
                // Check whether the current thread is interrupted.
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println("Thread 1 receives interrupt message, interrupt thread...");
                    break;
                }
                System.out.println(Thread.currentThread().getName() + "Thread executing..."); }},"interrupt-1");
        // Start thread 1
        thread.start();

        // Create interrupt-2 threads
        new Thread(() -> {
            int i = 0;
            while (i <20){
                System.out.println(Thread.currentThread().getName()+"Thread executing...");
                if (i == 8){
                    System.out.println("Set thread interrupt....");
                    Thread 1 sets interrupt notification
                    thread.interrupt();
                }
                i ++;
                try {
                    TimeUnit.MILLISECONDS.sleep(1);
                } catch(InterruptedException e) { e.printStackTrace(); }}},"interrupt-2").start();
    }
Copy the code

The print result is as follows:

As you can see from the code above, we create an interrupt-1 thread that uses interrupt to determine whether the current thread is interrupted or not. If interrupted, the thread is terminated. To create an interrupt-2 thread, the code is relatively simple and unexplained. When execution reaches a certain point, the interrupt-1 thread is set to the interrupted state, that is, the interrupt-1 thread is notified.

Reset thread interrupt flag:

If sleep is added to InterruptedException, interrupt-1 will not stop because the interrupt flag is reset. Let’s take a look at the contents related to interrupt mark reset

  • ** ** is provided in the thread classThread.interruptedIn the code above, we can make a small change to the thread interrupt flaginterrupt-1The code for thread creation is modified as follows:
        // Create interrupt-1 threads

        Thread thread = new Thread(() -> {
            while (true) {
                // Check whether the current thread is interrupted.
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println("Thread 1 received an interrupt message, the interrupt thread... Interrupt flag:" + Thread.currentThread().isInterrupted());
                    Thread.interrupted(); // // resets the thread from true to false
                    System.out.println("After thread.interrupted () is reset, interrupt marks:" + Thread.currentThread().isInterrupted());
                    // Again determine whether to interrupt, if so, exit the thread
                    if (Thread.currentThread().isInterrupted()) {
                        break;
                    }
                }
                System.out.println(Thread.currentThread().getName() + "Thread executing..."); }},"interrupt-1");
Copy the code

As you can see from the above code, the flag is true to determine whether the current thread is interrupted, or true if notified by another program. Then enter the if statement, reset it, and decide again. After executing the code, we find that the interrupt-1 thread does not terminate, but continues to execute

  • Thread.interruptedThread interrupt flag reset is an active action, but there is also a passive reset scenario, that is, when the program appearsInterruptedExceptionWhen an exception occurs, the interrupt flag state of the current thread is reset. Before an exception is thrown,JVMWill mark the interruptisInterruptedSet tofalse

In the program, the existence of thread interrupt reset is actually a response of the current thread to the external interrupt notification signal, but the specific response content is determined by the current thread, the thread will not stop immediately, the specific stop is decided by the current thread itself, that is, the developer.


3.3 How threads terminate Interrupts

  • First let’s take a look at theThreadIn aboutinterruptDefinition:
    public void interrupt(a) {
        if (this! = Thread.currentThread()) { checkAccess();// Verify that you have permission to modify the current thread

            // thread may be blocked in an I/O operation
            synchronized (blockerLock) {
                Interruptible b = blocker;
                if(b ! =null) {
                    // <1> Call native methods
                    interrupt0();  // set interrupt status
                    b.interrupt(this);
                    return; }}}// set interrupt status
        interrupt0();
    }
Copy the code
  • As we can see in the code above, theinterruptMethod is finally calledNativemethodsinterrupt0The correlation here was said at the start of the threadhotspotjvm.cppIn the fileJVM_Interruptmethods

  • JVM_InterruptThe method is relatively simple, where we can see the direct callThread.cppinterruptMethod, we go into it to see

  • And we can see that it’s called directly here os::interrupt(thread)Here is called platform method, for different platform implementation is different, we here as shown below, selectLinuxWith the implementation of theos_linux.cpp,


If interrupt is false, set OSThread set_interrupted () to 2. If interrupt is false, set OSThread set_interrupted () to 2. OsThread. HPP defines a volatile jint _interrupted member variable. The set_interrupted method simply sets _interrupted to true and then wakes up the thread using the unpark() method of ParkEvent. The specific process is described in simple notes above,


This article was published by AnonyStar. It may be reproduced but the original source must be claimed. Admire “the art of elegant coding” firmly believe that practice makes perfect, and strive to change your life