Start and end of thread

1, Thread creation

1.Oracle official website description

There are two ways to create a new thread of execution.

One is to declare a class to be a subclass of Thread. This subclass should override the run method of class Thread. An instance of the subclass can then be allocated and started.

The other way to create a thread is to declare a class that implements the Runnable interface. That class then implements the run method. An instance of the class can then be allocated, passed as an argument when creating Thread, and started.

There are two ways to create a new execution Thread. One method is to define a subclass of Thread class, which overrides the run method of Thread class. You can then assign and start instances of subclasses. Another way to create a Thread is to define a class that implements the Runnable interface, and then you can assign an instance of that class, pass it as a parameter and start it when you create a Thread.

1.1 inherit Thread class

public class ThreadStyle extends Thread {

    @Override
    public void run() {
        System.out.println("use Thread Method to create a thread");
    }

    public static void main(String[] args) {
        new ThreadStyle().start();
    }
}

Define a class to inherit the Thread class, and then override the run method to start the Thread directly through the instance of the class. The output is: create the Thread by Thread.

1.2 implementation of Runnable interface

public class RunnableStyle implements Runnable {

    @Override
    public void run() {
        System.out.println("use Runnable Mode implementation thread");
    }

    public static void main(String[] args) {
        new Thread(new RunnableStyle()).start();
    }
}

Define a class to implement the Runnable interface and override the run method. Pass the Runnable instance to the Thread class parameter to start the Thread. The output is: to implement the Thread in the Runnable mode.

1.3 comparison of two methods

To be exact, Thread creation is realized by constructing Thread class. There are two ways to implement Thread execution unit.

public class Thread implements Runnable {
    /** ellipsis */
    
    private Runnable target;
    
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
}
  • Method 1 (inherit Thread class): the Thread class itself also implements the Runnable interface and rewrites the run method. When creating a Thread by inheriting the Thread class, the entire run method of the Thread class will be rewritten.
  • Method 2 (implementation of Runnable interface): there is a Runnable variable named target in the Thread class. Passing in the Runnable instance in the Thread(Runnable target) construction method will initialize the target attribute. Creating a Thread through this method will only call the target.run() method, and will not override the entire run method.

The way to create a thread through method 2 is actually better:

  • The way to implement the Runnable interface is decoupled from the Thread class.
  • The interface can be implemented in multiple ways, and the way of inheriting Thread class limits the scalability.
  • If the Thread class is inherited, each time a new Thread is created, it will create an independent Thread, which is expensive and not suitable for resource sharing. When the Runnable interface is implemented, it is easy to realize resource sharing, and it can use tools such as Thread pool to greatly reduce the loss of creating and destroying threads.

Use both methods to create threads:

First, the Thread is created by passing in the anonymous inner class (Runnable instance) in the Thread class constructor, and then the run method of the Thread class is rewritten on this basis. The final output is: create by Thread. Because the incoming Runnable instance creation Thread is executed by calling target.run() in the run method, but the run method is overridden later, causing the method to fail.

public class BothRunnableAndThread {

    public static void main(String[] args) {
        new Thread(() -> System.out.println("Use Runnable Way to create")) {
            // Rewrite the run method, covering three lines of code in run
            // runnable passed in but not running
            @Override
            public void run() {
                System.out.println("Use Thread Way to create");
            }
        }.start();
    }
}

There are two ways to create a Thread. In addition, you can create a Thread by implementing the Callable interface and using the Thread pool, but in essence, you can inherit the Thread class or implement the Runnable interface.

1.4 thread initialization

No matter which way to create a thread, the thread initialization function init will be called. Some parameters of the thread can be initialized through different constructors. As shown in the init function below, a newly constructed thread object is allocated space by its parent thread, while the child thread inherits the parent thread's attributes such as day and priority, and also assigns a unique id to identify the thread.

public class Thread implements Runnable {
    
    public Thread() {
        init(null, null, "Thread-" + nextThreadNum(), 0);
    }
    
    public Thread(String name) {
        init(null, null, name, 0);
    }
    
    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
    
    public Thread(Runnable target, String name) {
        init(null, target, name, 0);
    }
    
    private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) {
        // Omit some codes
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }
        this.name = name;
        // The current thread is the parent of the thread
        Thread parent = currentThread();
        // Copy the parent thread's day and priority properties
        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
        this.target = target;
        setPriority(priority);
        // Copy the inheritablethreadlocales of the parent thread
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        // Assign thread ID
        tid = nextThreadID();
    }
}

2. Implement Callable interface

public class CallableDemo implements Callable<String> {

    @Override
    public String call() {
        return "hncboy";
    }

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        FutureTask<String> ft = new FutureTask<>(new CallableStyle());
        Thread thread = new Thread(ft);
        thread.start();
        System.out.println(ft.get());
    }
}

3. Use thread pool

public class ThreadPoolDemo {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        executorService.submit(new TaskThread());
    }

    private static class TaskThread implements Runnable {

        @Override
        public void run() {
            System.out.println("hncboy");
        }
    }
}

4. Use timer

public class TimerTaskDemo {

    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                System.out.println("hncboy");
            }
        }, 1000, 1000);
    }
}

2, Start of thread

1.start() method

1.1 method meaning

Start a new thread: informs the JVM to start a thread when it is idle. In essence, it requests the JVM to run our thread. When the thread runs is determined by the thread scheduler. When the thread starts, two threads will be started: the first is the parent thread or the main thread used to execute the start method, and the second is the created child thread.

Preparation: make the thread in ready state (other resources other than CPU have been obtained, such as context, thread state, stack, etc.) before it can be scheduled by JVM or operating system to the execution state to obtain CPU resources, and then run method can be executed.

Repeat call to start(): throw Exception in thread "main" java.lang.IllegalThreadStateException. Once the thread starts, it will enter other states from NEW state, such as RUNNABLE. Only the thread in NEW state can call start() method.

1.2 principle analysis

The threadStatus property is used to determine whether to start repeatedly and throw an exception. The actual starting method is the native method start0().

public class Thread implements Runnable {
    
    /**
     * Thread status, initialized to 0, indicating that it has not been started
     */
	private volatile int threadStatus = 0;
    
	public synchronized void start() {
        // Judge the state of the thread, that is to say, judge whether to start or not. Throw IllegalThreadStateException when repeatedly starting
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        // Join a thread to a thread group
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    // Tell thread group that the thread failed to start
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {}
        }
    }
	
    private native void start0();
}

adopt /src/share/native/java/lang/Thread.c As you can see, the start0() method corresponds to the JVM_StartThread method

static JNINativeMethod methods[] = {
    {"start0",           "()V",        (void *)&JVM_StartThread},
};

Be located /src/hotspot/share/prims/jvm.cpp Section comment in JVM? Startthread method of

// Since JDK 5 the java.lang.Thread threadStatus is used to prevent
// re-starting an already started thread, so we should usually find
// that the JavaThread is null. However for a JNI attached thread
// there is a small window between the Thread object being created
// (with its JavaThread set) and the update to its threadStatus, so we
// have to check for this

The comment in this section says that since JDK5, the Thread status property of the Thread class has been used to restart threads. Next, let's look at /src/share/vm/runtime/thread.cpp start method in which it is judged that if the thread is a Java thread, the status of the thread will be changed to RUNNABLE.

void Thread::start(Thread* thread) {
  trace("start", thread);
  if (!DisableStartThread) {
    if (thread->is_Java_thread()) {
	 // The set thread status method is called here to change the thread status to RUNNALBE
      java_lang_Thread::set_thread_status(((JavaThread*)thread)->threadObj(),
                                          java_lang_Thread::RUNNABLE);
    }
    os::start_thread(thread);
  }
}

2.run() method

run() is just a basic method of Thread class

public class Thread implements Runnable {
    /** ellipsis */
    
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
}

3. Compare the two methods

Output: main and Thread-0

public class StartAndRunMethod {

    public static void main(String[] args) {
        Runnable runnable = () -> System.out.println(Thread.currentThread().getName());
        runnable.run();
        new Thread(runnable).start();
    }
}

To call start method is to start a thread in a real sense, and it will go through various life cycles of the thread. If you call run method directly, it is just a common method, not a sub thread.

3, Thread termination

1. Expired suspend(), resume(), stop()

These three methods have been abolished by checking Official Oracle documentation As you can see. Using stop() method to stop a thread will release all monitors of the thread. When terminating a thread, this method will not guarantee the normal release of the thread's resources, and throw ThreadDeath exception. Usually, it does not give the thread the opportunity to complete the resource release work, so it will cause the program data to be out of sync. The suspend () method is easy to cause deadlock. After the method is called, the thread will not release the occupied resources (such as locks), but will enter the suspended state with the occupied resources. resume() must be used with suspend(). When the thread to recover the target thread attempts to lock the monitor before calling resume, it will cause deadlock.

2.volatile flag bit

The shared variable decorated by volatile can terminate the thread.

2.1 success stories

Sub thread output every 1 second: running continuously. After 2 seconds, the main thread sets stop to true. At this time, the while loop of the sub thread stops and the running of the sub thread ends. The cycle is only carried out twice, and the operation results are as follows:

Continuous operation
 Continuous operation
public class RightVolatileDemo {

    private static volatile boolean stop = false;

    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            while (!stop) {
                System.out.println("Continuous operation");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        TimeUnit.SECONDS.sleep(2);
        stop = true;
    }
}

2.3 failure cases

The limitation of using volatile is that when a thread gets stuck in a block, variables decorated with volatile cannot stop the thread.

Through the producer consumer example to demonstrate the limitations of volatile in the case of blocking, a producer class is defined to implement the Runnable interface overriding the run method. In run, when the cancelled variable modified by volatile is false, the producer continuously adds data through the put method of BlockingQueue. When the blocking queue reaches the upper limit, the put method will block. Define a consumer class, and judge whether the consumer ends consumption by needMoreCount method.

In the main function, initialize a blocking queue with a length of 10 to build the producer and consumer instances. When the consumer ends consumption, change the value of the cancelled attribute of the producer to true, but the producer is still running at this time, because the producer thread is blocked in the put method. This is the limitation of the volatile flag bit.

public class WrongVolatileDemo {

    public static void main(String[] args) throws InterruptedException {
        // Define blocking queue with capacity of 10
        BlockingQueue<Integer> storage = new ArrayBlockingQueue<>(10);

        // Start producer thread
        Thread producerThread = new Thread(new Producer(storage));
        producerThread.start();
        Thread.sleep(1000);

        // Launch consumer
        Consumer consumer = new Consumer(storage);
        while (consumer.needMoreCount()) {
            System.out.println("Consumer consumption:" + consumer.getStorage().take());
            Thread.sleep(100);
        }
        System.out.println("Consumer spending is over");

        // At this time, the producer should not continue to produce
        Producer.canceled = true;
    }

    /**
     * Producer
     */
    private static class Producer implements Runnable {

        static volatile boolean canceled = false;
        private BlockingQueue<Integer> storage;

        public Producer(BlockingQueue<Integer> storage) {
            this.storage = storage;
        }

        @Override
        public void run() {
            int count = 1;
            try {
                while (!canceled) {
                    // If the queue is full, the put method blocks the current thread
                    storage.put(count);
                    System.out.println("Producer production:" + count);
                    count++;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                System.out.println("Producer stopped");
            }
        }
    }

    /**
     * Consumer
     */
    private static class Consumer {

        private BlockingQueue<Integer> storage;

        public Consumer(BlockingQueue<Integer> storage) {
            this.storage = storage;
        }

        public BlockingQueue<Integer> getStorage() {
            return storage;
        }

        public boolean needMoreCount() {
            return Math.random() < 0.95;
        }
    }
}

3.interrupt method

Interrupt is translated into interrupt. Interrupt can be understood as an identity bit property of a thread, which indicates whether a running thread has been interrupted by other threads. Interrupts are like other threads saying hello to the thread. Other threads interrupt the thread by calling its interrupt() method.

Here are a few examples to illustrate the different uses of interrupt.

3.1 interruption without blocking

This example is the simplest interrupt. After the thread thread is started, it will sleep for 1ms and then call the interrupt method of the object. At this time, the executing loop in the thread detects that Thread.currentThread().isInterrupted() is true to end the loop and output the value of count variable.

When a thread calls its interrupt method, it will set the interrupt flag to true, and the internal loop of the thread will end the loop by checking whether it is interrupted, while the internal isInterrupted() method of the thread can determine whether the thread is interrupted.

public class InterruptThreadWithoutSleep {
    
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            int count = 0;
            // Check if it is interrupted to end the cycle
            while (!Thread.currentThread().isInterrupted()) {
                count++;
            }
            System.out.println(count);
        });
        thread.start();
        Thread.sleep(1);
        // Set interrupt flag
        thread.interrupt();
    }
}

3.2 interrupt with blocking

This example demonstrates the use of interrupt methods with sleep blocking. The sleep method needs to throw InterruptedException, indicating that the method can respond to interrupt interrupt. After the thread is started, the thread will sleep for 1s, and the main thread will call the interrupt method after sleeping for 100ms. At this time, the thread is in the blocking state. In response to the interrupt in the blocking state, the sleep method will throw the InterruptedException. However, before throwing the exception, the JVM will clear the thread's interrupt flag bit first, and then throw the InterruptedException. At this time, call Using the isInterrupted() method will return false.

If the sleep method is called in every loop during execution, it is unnecessary to check the interrupt through isInterrupted() method in every iteration, because the sleep method will respond to the interrupt.

public class InterruptThreadWithSleep {

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("Interrupt flag:" + Thread.currentThread().isInterrupted());
            }
        });
        thread.start();
        Thread.sleep(100);
        thread.interrupt();
    }
}

The operation results are as follows:

java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at com.hncboy.interrupt.InterruptThreadWithSleep.lambda$main$0(InterruptThreadWithSleep.java:15)
	at java.lang.Thread.run(Thread.java:748)
//Interrupt flag: false

3.3 interrupt related methods

3.4.1 interrupt()

Set the interrupt flag, and finally call the native interrupt0() method to set the interrupt flag.

public void interrupt() {
    if (this != Thread.currentThread())
        // Permission check
        checkAccess();
	
    synchronized (blockerLock) {
        // IO read write correlation
        Interruptible b = blocker;
        if (b != null) {
            interrupt0();
            b.interrupt(this);
            return;
        }
    }
    // This method will definitely execute
    interrupt0();
}

private native void interrupt0();

Find the JVM "interrupt method corresponding to the interrupt0 method, and find the method code.

JVM_ENTRY(void, JVM_Interrupt(JNIEnv* env, jobject jthread))
  JVMWrapper("JVM_Interrupt");

  // Ensure that the C++ Thread and OSThread structures aren't freed before we operate
  oop java_thread = JNIHandles::resolve_non_null(jthread);
  MutexLockerEx ml(thread->threadObj() == java_thread ? NULL : Threads_lock);
  // We need to re-resolve the java_thread, since a GC might have happened during the
  // acquire of the lock
  JavaThread* thr = java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread));
  if (thr != NULL) {
    Thread::interrupt(thr);
  }
JVM_END

Find the code for the key method Thread::interrupt.

void Thread::interrupt(Thread* thread) {
  trace("interrupt", thread);
  debug_only(check_for_dangling_thread_pointer(thread);)
  os::interrupt(thread);
}

Find the code of the key method os::interrupt. At this time, find the method to set the interrupt flag. Each thread in Java corresponds to the thread of the operating system one by one. An osthread corresponds to a thread in Java. If the osthread is not set to interrupt, set the interrupt flag to true.

void os::interrupt(Thread* thread) {
  assert(Thread::current() == thread || Threads_lock->owned_by_self(),
    "possibility of dangling Thread pointer");

  OSThread* osthread = thread->osthread();
  // If the thread is not interrupted 
  if (!osthread->interrupted()) {
    // Set interrupt flag to true
    osthread->set_interrupted(true);
    // More than one thread can get here with the same value of osthread,
    // resulting in multiple notifications.  We do, however, want the store
    // to interrupted() to be visible to other threads before we execute unpark().
    OrderAccess::fence();
    ParkEvent * const slp = thread->_SleepEvent ;
    if (slp != NULL) slp->unpark() ;
  }

  // For JSR166. Unpark even if interrupt status already was set
  if (thread->is_Java_thread())
    ((JavaThread*)thread)->parker()->unpark();

  ParkEvent * ev = thread->_ParkEvent ;
  if (ev != NULL) ev->unpark() ;

}

3.4.2 isInterrupted() and interrupted()

Returns the thread's interrupt status. interrupted is a static method. Both methods call isInterrupted method, but the parameters passed in are different. true means to clear the interrupt state, and false means not to clear.

Thread.interrupted() returns the interrupt flag of the thread in which it is called.

public boolean isInterrupted() {
    return isInterrupted(false);
}
  
public static boolean interrupted() {
    return currentThread().isInterrupted(true);
}

private native boolean isInterrupted(boolean ClearInterrupted);

3.4.2 comprehensive example

public class InterruptComprehensive {

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {});

        // Startup thread
        thread.start();
        // Set interrupt flag
        thread.interrupt();
        // Get the interrupt flag and return true if it is interrupted
        System.out.println("isInterrupted: " + thread.isInterrupted());
        // Get the interrupt flag and reset it. The interrupted static method calls the thread that executes it, that is, the main thread, and returns false
        System.out.println("isInterrupted: " + thread.interrupted());
        // Get the interrupt flag and reset it. The Main function is not interrupted and returns false
        System.out.println("isInterrupted: " + Thread.interrupted());
        // Get the interrupt flag. If the interrupt flag is not cleared, return true
        System.out.println("isInterrupted: " + thread.isInterrupted());
        thread.join();
        System.out.println("Main thread is over.");
    }
}

Operation result:

isInterrupted: true
isInterrupted: false
isInterrupted: false
isInterrupted: true
Main thread is over.

3.4 partial methods that can respond to interruption

  • Object.wait()/wait(long)/wait(long, int)
  • Thread.sleep(long)/sleep(long, int)
  • Thread.join()/join(long)/join(long, int)
  • java.util.concurrent.BlockingQueue.take()/put(E)
  • java.util.concurrent.locks.Lock.lockInterruptibly()
  • java.util.concurrent.CountDownLatch.await()
  • java.util.concurrent.CyclicBarrier.await()
  • java.util.concurrent.Exchanger.exchange(V)
  • java.nio.channels.InterruptibleChannel related methods
  • java.nio.channels.Selector related methods

3.5 InterruptedException exception handling

3.5.1 transfer interruption

When an exception method is invoked in run, the exception should be declared in throws in the method and passed to the run method instead of being captured in the method. This may cause unpredictable results. Throinmethod2() is the correct way, throinmethod1() is the wrong way.

public class HandleInterruptedException implements Runnable {

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new HandleInterruptedException());
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }

    @Override
    public void run() {
        while (true) {
            System.out.println("work");
            try {
                throwInMethod2();
            } catch (InterruptedException e) {
                System.out.println("Save log, stop program");
                e.printStackTrace();
            }
        }

        /*while (true) {
            System.out.println("go");
            throwInMethod1();
        }*/
    }

    private void throwInMethod2() throws InterruptedException {
        Thread.sleep(2000);
    }

    private void throwInMethod1() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

3.5.2 reset interrupt status

When an InterruptedException exception is thrown because of a block, the interrupt state is cleared. The Thread.currentThread().interrupt() method can be used to restore the interrupt state in the catch subclause, so that interruption can be detected in subsequent execution.

public class HandleInterruptedException2 implements Runnable {

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new HandleInterruptedException2());
        thread.start();
        Thread.sleep(1000);
        thread.interrupt();
    }

    @Override
    public void run() {
        while (true) {
            if (Thread.currentThread().isInterrupted()) {
                System.out.println("Interruption occurred, program operation ended");
                break;
            }
            System.out.println("work");
            reInterrupt();
        }
    }

    private void reInterrupt() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            e.printStackTrace();
        }
    }
}

The art of Java Concurrent Programming

Deep understanding of the underlying implementation of Thread.run()

Thread.interrupt() related source code analysis

Brilliant life
WeChat scan two-dimensional code, pay attention to my public number.
Published 10 original articles, won praise and 398 visitors
Private letter follow

Tags: Java jvm Oracle Attribute

Posted on Thu, 16 Jan 2020 06:05:17 -0500 by echoindia756