What is a thread

Modern operating systems create a process for a program when it runs, for example by launching a Java program. The smallest unit of scheduling in modern operating systems is threads, also known as lightweight processes. Multiple threads can be created within a process, each with its own attributes such as counters, stacks, and local variables, and have access to shared memory variables. The processor switches between these threads at high speed, giving the user the impression that they are running simultaneously.

A Java program starts with the main() method and then executes according to the established code logic. It looks like no other thread is involved, but Java programs are naturally multithreaded because the main() method is executed by a thread named main. You can use the following code to see what threads a normal Java program contains:

public class MultiThread { public static void main(String[] args) { ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); ThreadInfo[] threadInfos = threadBean.dumpAllThreads(false, false); for (ThreadInfo threadInfo : threadInfos) { System.out.println("[" + threadInfo.getThreadId() + "] " + threadInfo.getThreadName()); }}}Copy the code

Running results:

[5] Attach Listener[4] Signal Dispatcher[3] Finalizer[2] Reference Handler[1] main
Copy the code

As you can see, a Java program is not just running the main() method, but the main thread and multiple other threads running simultaneously.

Why multithreading

Does executing a simple Java program complicate a simple problem by starting so many “irrelevant” threads? Of course not, because using multithreading correctly always brings significant benefits, and there are several main reasons for using multithreading:

  1. More processor cores

With the increasing number of cores on a processor and the widespread use of hyper-threading technology, computers today are better at parallel computing than ever before, and the way processor performance is improved is from higher frequency to more cores.

Threads are the basic unit of scheduling in most systems. A program is run as a process, with the ability to create multiple threads while the program is running, and a thread can only run on one processor at a time. Imagine that a single-threaded program can run with only one processor. No amount of processors on a computer can significantly improve the program’s performance. In contrast, with multithreading, dividing the computational logic across multiple processors significantly reduces program processing time and can become more efficient as more processors are added

  1. Faster response times

Sometimes we write complex code, such as creating an order, which includes inserting order data, creating a snapshot of the order, sending an email to notify the seller, and recording the number of items sold. From the moment the user clicks the submit button, they have to wait for the completion of all these operations to see the result of the successful order, but with so much business, how can it be completed faster?

In the above scenario, we can use multi-threading to dispatch operations with inconsistent data to other threads for processing, such as generating order snapshots, sending emails, etc. The advantage of this is that the threads responding to user requests can complete as quickly as possible, shortening response time and improving user experience

  1. A better programming model

Java provides a good programming model for multithreaded programming, allowing developers to focus more on problem solving, that is, building the right model for the problem they encounter, rather than racking their brains to think about how to multithread it. Once the developer has established the model, a few changes can always be easily mapped to the multithreaded programming model provided by Java.

Thread state

A Java thread may be in six states during its life cycle as listed in the table below, and at any given moment a thread can be in only one of them:

| | | state name

|–|–|

| NEW | initial state, the thread is build, but no | call start () method

| RUNNABLE | operation state, Java thread will be ready in the operating system and run the two states generally referred to as “running” |

| BLOCKED | the blocking state, said | thread block in the lock

| WAITING | wait state, said thread into the WAITING, the state of the said thread needs to wait for other threads to make some special action (notice or interrupt) |

| TIME_WAITING | timeout WAITING state, the state is different from the WAITING, he can be in the specified time to return |

| TERMINATED | end state, according to the current thread | already has been completed

Here we can use the jstack command to try to see the thread information when the sample code is running:

public class ThreadState {	public static void main(String[] args) {		new Thread(() ->  {			while(true) {				try {					TimeUnit.SECONDS.sleep(100);				} catch (InterruptedException e) {					e.printStackTrace();				}			}		}, "TimeWaitingThread").start();				new Thread(new Waiting(), "WaitingThread").start();		new Thread(new Blocked(), "BlockedThread - 1").start();		new Thread(new Blocked(), "BlockedThread - 2").start();	}		static class Waiting implements Runnable {		@Override		public void run() {			while(true) {				synchronized(Waiting.class) {					try {						Waiting.class.wait();					} catch (InterruptedException e) {						e.printStackTrace();					}				}			}		}			}		static class Blocked implements Runnable {		@Override		public void run() {			synchronized (Blocked.class) {				while(true) {					try {						TimeUnit.SECONDS.sleep(100);					} catch (InterruptedException e) {						// TODO Auto-generated catch block						e.printStackTrace();					}				}			}		}	}}
Copy the code

Open the command terminal and type JPS. The output is as follows:

! [](https://static001.geekbang.org/infoq/f3/f31a11ab896bc12de3e9b12906515908.png)

You can see that the thread ID for running the example is 1764, and then enter jStack 1764, as shown below:

C:\Users\ XXX > jStack 17642019-05-14 08:52:54Full Thread Dump Java HotSpot(TM) 64-bit Server VM (25.112-b15 mixed mode):"DestroyJavaVM" #13 prio=5 os_prio=0 tid=0x000000000231f000 nid=0x2838 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE"BlockedThread - 2" #12 prio=5 os_prio=0 tid=0x000000005870d000 nid=0x2660 waiting for monitor entry [0x000000005908f000] java.lang.Thread.State: BLOCKED (on object monitor) at com.yrk.concurrent.ThreadState$Blocked.run(ThreadState.java:46)waiting to lock <0x00000000d6031f38> (a java.lang.Class for com.yrk.concurrent.ThreadState$Blocked) at java.lang.Thread.run(Thread.java:745) "BlockedThread - 1" #11 prio=5 os_prio=0 tid=0x0000000058706000 nid=0x32cc waiting  on condition [0x0000000058f5e000] java.lang.Thread.State: TIMED_WAITING (sleeping) at java.lang.Thread.sleep(Native Method) at java.lang.Thread.sleep(Thread.java:340) at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386) at com.yrk.concurrent.ThreadState$Blocked.run(ThreadState.java:46)locked <0x00000000d6031f38> (a java.lang.Class for com.yrk.concurrent.ThreadState$Blocked) at java.lang.Thread.run(Thread.java:745) "WaitingThread" #10 prio=5 os_prio=0 tid=0x0000000058705800 nid=0x2824 in Object.wait() [0x000000005896f000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method)waiting on <0x00000000d6030690> (a java.lang.Class for com.yrk.concurrent.ThreadState$Waiting) at java.lang.Object.wait(Object.java:502) at com.yrk.concurrent.ThreadState$Waiting.run(ThreadState.java:30)locked <0x00000000d6030690> (a java.lang.Class for com.yrk.concurrent.ThreadState$Waiting) at java.lang.Thread.run(Thread.java:745)"TimeWaitingThread" #9 prio=5 os_prio=0 tid=0x00000000586ea000 nid=0x318c waiting on condition [0x0000000058d7e000] java.lang.Thread.State: TIMED_WAITING (sleeping) at java.lang.Thread.sleep(Native Method) at java.lang.Thread.sleep(Thread.java:340) at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386) at com.yrk.concurrent.ThreadState.lambda$0(ThreadState.java:11) at com.yrk.concurrent.ThreadState$Lambda$1/834600351.run(Unknown Source) at java.lang.Thread.run(Thread.java:745)Copy the code

Through the example, we understand the specific meaning of thread state in Java program running. Threads are not in a fixed state during their life cycle, but switch between different states with the execution of the code. The Java thread state changes as shown in the following figure:

! [](https://static001.geekbang.org/infoq/3b/3bcb17206e10bbd00a8cd86aaa574e57.png)

After a thread is created, it calls the start() method to start running. After a thread calls the wait() method, it enters the wait state. A thread in wait state can only return to running state by notification from other threads, and timeout wait state is equivalent to adding timeout limit on the basis of waiting state, that is, it will return to running state when the timeout time reaches. When a thread calls a synchronous method, it blocks without acquiring the lock. The thread enters the terminating state after executing Runnable’s run method.

Starting a thread

After the thread object is initialized, the start() method is called to start the thread; The thread start() method means that the current thread tells the JVM to start the thread that called the start() method.

One of the things we might wonder about is why we call the start method instead of the run method to start a thread. A quick look at the definition of the start method:

Public class Thread implements Runnable {// Omits part of the code public synchronized void start() {/**This method is not invoked for  the main method thread or "system"group threads created/set up by the VM. Any new functionality addedto 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 startedso 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 { 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 */ } } } private native void start0(); }Copy the code

We can see that the start method actually calls a native method called start0() to start a Thread. Start0 () is registered in the static block of Thread:

public class Thread implements Runnable { / Make sure registerNatives is the first thing <clinit> does. / private static  native void registerNatives(); static { registerNatives(); } // omit code}Copy the code

The registerNatives local method is defined in the thread.c file; Thread.c defines common data and operations on threads for each operating system platform. Here’s what thread. c is all about:

Hg.openjdk.java.net/jdk8/jdk8/j…

static JNINativeMethod methods[] = { {"start0", "()V", (void *)&JVM_StartThread}, {"stop0", "(" OBJ ")V", (void *)&JVM_StopThread}, {"isAlive", "()Z", (void *)&JVM_IsThreadAlive}, {"suspend0", "()V", (void *)&JVM_SuspendThread}, {"resume0", "()V", (void *)&JVM_ResumeThread}, {"setPriority0", "(I)V", (void *)&JVM_SetThreadPriority}, {"yield", "()V", (void *)&JVM_Yield}, {"sleep", "(J)V", (void *)&JVM_Sleep}, {"currentThread", "()" THD, (void *)&JVM_CurrentThread}, {"countStackFrames", "()I", (void *)&JVM_CountStackFrames}, {"interrupt0", "()V", (void *)&JVM_Interrupt}, {"isInterrupted", "(Z)Z", (void *)&JVM_IsInterrupted}, {"holdsLock", "(" OBJ ")Z", (void *)&JVM_HoldsLock}, {"getThreads", "()[" THD, (void *)&JVM_GetAllThreads}, {"dumpThreads", "([" THD ")[[" STE, (void *)&JVM_DumpThreads},};#undef THD#undef OBJ#undef STEJNIEXPORT void JNICALLJavajavalangThreadregisterNatives(JNIEnv  *env, jclass cls){ (*env)->RegisterNatives(env, cls, methods, ARRAY_LENGTH(methods));}Copy the code

As you can see from the above code, start0() actually executes the JVM_StartThread method. What does this do? Start a thread in the JVM. Need to download the HotSpot source hg.openjdk.java.net/jdk8u/jdk8u… Then find the jvm.cpp file:

JVMENTRY(void, JVMStartThread(JNIEnv* env, jobject jthread)) JVMWrapper("JVM_StartThread"); . sizet sz = size > 0 ? (sizet) size : 0; nativethread = new JavaThread(&threadentry, sz); .Copy the code

In this method we create a new JavaThread, and in the thread. CPP file we find Thread::Java

JavaThread::JavaThread(ThreadFunction entrypoint, sizet stack_sz) : Thread()#if INCLUDEALLGCS , satbmarkqueue(&satbmarkqueue_set), dirtycardqueue(&dirtycardqueue_set)#endif // INCLUDEALLGCS{ if (TraceThreadEvents) { tty->print_cr("creating thread %p",  this); } initialize(); jniattachstate = notattachingvia_jni; setentrypoint(entry_point); os::ThreadType thrtype = os::javathread; thrtype = entrypoint == &compilerthreadentry ? os::compiler_thread : os::java_thread; os::createthread(this, thrtype, stack_sz); }Copy the code

This method takes two arguments: the first is the name of the function that will be called when the thread is successfully created, and the second is the number of existing threads in the current process. Finally, we’ll focus on OS ::create_thread, which actually creates a thread by calling the platform’s thread creation method. CPP file Thread::start(Thread* Thread) :

void Thread::start(Thread* thread) { trace("start", thread); // Start is different from resume in that its safety is guaranteed by context or // being called from a Java method synchronized on the Thread object. if (! DisableStartThread) { if (thread->isJavathread()) { // Initialize the thread state to RUNNABLE before starting this thread. // Can not set it after the thread started because we do not know the // exact thread state at that time. It could be in MONITOR_WAIT or // in SLEEPING or some other state. javalangThread::setthreadstatus(((JavaThread*)thread)->threadObj(), javalangThread::RUNNABLE); } os::start_thread(thread); }}Copy the code

The OS ::start_thread() method is called in the start method, which calls the platform method that started the Thread, and ultimately calls the javathRead-run () method in Thread.cpp.

Termination of a thread

Starting a thread is familiar, so how do you terminate a thread?

Terminating a thread is not done by simply calling the stop command. Although the stop API can still be called, it is not recommended because it is outdated, as are other thread control methods such as suspend and resume. In the case of stop, terminating a thread does not guarantee that the thread’s resources will be released, thus causing some indeterminate state in the program.

To gracefully interrupt a thread, provide an interrupt method in the thread. When another thread calls the current thread’s interrupt method, it signals to the current thread that it is ready to interrupt its execution. When to interrupt is up to the thread. A thread responds by checking whether it has been interrupted, using the isInterrupted() method.

public class InterruptedDemo { private static int i; public static void main(String[] args) throws Exception { Thread thread = new Thread(() -> { while (! Thread.currentThread().isInterrupted()) { i++; } }, "interruptedThread"); thread.start(); TimeUnit.SECONDS.sleep(1); thread.interrupt(); }}Copy the code

This approach, by identifying bits or interrupting operations, gives the thread a chance to clean up when it terminates, rather than arbitrarily terminating the thread, making it safer and more elegant.

In the above example, interrupt sets a flag to tell the Thread that it has terminated, and the Thread also provides a static method thread.interrupted () to reset the Thread that set the interrupt flag. For example, in the following example, the outer Thread calls Thread.interrupt() to set the interrupt flag, while the inner Thread uses thread.interrupt () to reset the interrupt flag:

public class InterruptedDemo {	private static int i;		public static void main(String[] args) throws Exception {		Thread thread = new Thread(() ->  {			while (true) {				if (Thread.currentThread().isInterrupted()) {					System.out.println("before interrputed(): " + Thread.currentThread().isInterrupted());					Thread.currentThread().interrupted();					System.out.println("after interrputed(): " + Thread.currentThread().isInterrupted());				}			}		}, "interruptedThread");		thread.start();		TimeUnit.SECONDS.sleep(1);		thread.interrupt();	}}
Copy the code

In addition to restoring the Thread interrupt identifier with thread.interrupted (), a passive reset scenario involves throwing InterruptedException before throwing InterruptedException The JVM clears the thread’s interrupt identifier before throwing InterruptedException, which returns false if the isInterrupted method is called.

Why reset

Thread.interrupted() is a response by the current Thread to a signal that it has been interrupted but does not interrupt immediately. The status of the current Thread remains false until it interrupts. That’s why you have to reset.

How threads terminate

Let’s look at what the thread.interrupt () method does:

public class Thread implements Runnable { public void interrupt() { if (this ! = Thread.currentThread()) checkAccess(); synchronized (blockerLock) { Interruptible b = blocker; if (b ! = null) { interrupt0(); // Just to set the interrupt flag b.interrupt(this); return; } } interrupt0(); }}Copy the code

Interrupt0 () is called in the interrupt() method, which we saw earlier in the start method and is a native method. Likewise, we find the JVM_Interrupt definition in the jVM. CPP file:

JVMENTRY(void, JVMInterrupt(JNIEnv* env, jobject jthread)) JVMWrapper("JVM_Interrupt"); // Ensure that the C++ Thread and OSThread structures aren't freed before we operate oop javathread = JNIHandles::resolvenon_null(jthread); MutexLockerEx ml(thread->threadObj() == javathread ? NULL : Threadslock); // We need to re-resolve the java_thread, since a GC might have happened during the // acquire of the lock JavaThread* thr = javalangThread::thread(JNIHandles::resolvenonnull(jthread)); if (thr ! = NULL) { Thread::interrupt(thr); }Copy the code

This method is relatively simple. It calls thread: : Interrupt (THR) directly. This method is defined in thread.cpp and looks like this:

void Thread::interrupt(Thread* thread) { trace("interrupt", thread); debugonly(checkfordanglingthread_pointer(thread);) os::interrupt(thread); }Copy the code

The Thread:: Interrupt method calls the OS :: Interrupt method, which calls the platform interrupt method. The implementation of this method is in the os_. CPP file, where _ represents different platforms. The way threads are scheduled is different. Take oslinux.cpp as an example:

void os::interrupt(Thread* thread) { assert(Thread::current() == thread || Threadslock->ownedby_self(), "possibility of dangling Thread pointer"); OSThread* OSThread = thread-> OSThread (); if (! Osthread ->interrupted()) {osthread->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->isJavathread()) ((JavaThread*)thread)->parker()->unpark(); ParkEvent * ev = thread->_ParkEvent ; if (ev ! = NULL) ev->unpark() ; }Copy the code

Set_interrupted (true) essentially calls the set_interrupted() method in osThread. CPP and defines a volatile jint_interrupted member attribute in osThread.

As you can see from the above analysis, thread.interrupt() actually sets the status flag to true and wakes up the thread with the unpark method of ParkEvent.

Why do Object.wait, Thread.sleep, and Thread.join throw InterruptedException?

One thing these methods have in common is that they are all blocking methods. A blocking method is released depending on some external event, but a blocking method may not be able to terminate because it cannot wait for an external trigger event, so it allows a thread to request itself to stop what it is doing. When a method throws InterruptedException, it tries to stop what it is doing and returns early by throwing InterruptedException.

So this exception means that a block is interrupted by another Thread, and since the Thread calls interrupt(), the blocked threads such as Object.wait and Thread.sleep() are woken up by is_interrupted. If an interrupt is found to be true, the interrupt identifier is cleared and InterruptedException is thrown.

It is important to note that throwing InterruptedException does not mean that the thread must terminate, but rather alerts the current thread that an interrupt has occurred. What happens next is up to the thread.