preface

After the Java Thread pool source code reading was published, many friends asked questions about Thread. Because Thread is the foundation of concurrency, today we’ll take a look at Thread at the source level around some issues.

In addition to the questions asked by my friends, I have also sorted out the following questions:

  • What is the difference between the run() and start() methods?
  • What if an asynchronous thread wants to return results?
  • When do thread deadlocks occur?
  • What is the life cycle of a thread?
  • What is the difference between sleep, join and wait?
  • interrupted(),isInterrupted(),stopThe difference between
  • Thread false death happens when, how to check?

Let’s start with a basic understanding of Thread.

Thread

For a primer on threads, see my previous article “Process Management for Operating Systems.”

A thread is a thread of execution in a program. The JVM allows applications to run multiple threads of execution simultaneously.

Each thread has a priority. The higher priority thread executes before the lower priority thread. Each thread may or may not be marked as a daemon thread. When code running in a thread creates a new thread object, the priority of the new thread is initially set to equal that of the thread that created it, and that thread is the daemon thread only if the creator thread is the daemon thread.

When the JVM is running, there is usually only one non-daemon thread (which usually calls the main method of a given class). The JVM continues to execute threads until one of the following occurs:

  • The Exit method of the Runtime class is called and the security manager has allowed the exit operation.
  • All non-daemon threads are dead, either by calling the run method to return or by throwing exceptions that propagate outside the run method.

Why does the Tomcat Web Application server always run?

The reason why the service can run all the time is that some of the threads are doing an “infinite” loop, which will never end until you end it manually, so Tomcat can always run in the background.

We can trace Tomcat’s Bootstrap main method. You’ll end up with an asynchronous method with a while loop in it:

public final class StandardServer extends LifecycleMBeanBase implements Server { /** * Wait until a proper shutdown command is received, then return. * This keeps the main thread alive - the thread pool listening for http * connections is daemon threads. */  @Override public void await() { // Negative values - don't wait on port - tomcat is embedded or we just don't like ports if (getPortWithOffset() == -2) { // undocumented yet - for embedding apps that are around, alive. return; } if (getPortWithOffset() == -1) { try { awaitThread = Thread.currentThread(); while(! stopAwait) { try { Thread.sleep( 10000 ); } catch( InterruptedException ex ) { // continue and check the flag } } } finally { awaitThread = null; } return; } // Set up a server socket to wait on try { awaitSocket = new ServerSocket(getPortWithOffset(), 1, InetAddress.getByName(address)); } catch (IOException e) { log.error(sm.getString("standardServer.awaitSocket.fail", address, String.valueOf(getPortWithOffset()), String.valueOf(getPort()), String.valueOf(getPortOffset())), e); return; } try { awaitThread = Thread.currentThread(); // Loop waiting for a connection and a valid command while (! stopAwait) { ServerSocket serverSocket = awaitSocket; if (serverSocket == null) { break; } // Wait for the next connection Socket socket = null; StringBuilder command = new StringBuilder(); try { InputStream stream; long acceptStartTime = System.currentTimeMillis(); try { socket = serverSocket.accept(); socket.setSoTimeout(10 * 1000); // Ten seconds stream = socket.getInputStream(); } catch (SocketTimeoutException ste) { // This should never happen but bug 56684 suggests that // it does. log.warn(sm.getString("standardServer.accept.timeout", Long.valueOf(System.currentTimeMillis() - acceptStartTime)), ste); continue; } catch (AccessControlException ace) { log.warn(sm.getString("standardServer.accept.security"), ace); continue; } catch (IOException e) { if (stopAwait) { // Wait was aborted with socket.close() break; } log.error(sm.getString("standardServer.accept.error"), e); break; } // Read a set of characters from the socket int expected = 1024; // Cut off to avoid DoS attack while (expected < shutdown.length()) { if (random == null) { random = new Random(); } expected += (random.nextInt() % 1024); } while (expected > 0) { int ch = -1; try { ch = stream.read(); } catch (IOException e) { log.warn(sm.getString("standardServer.accept.readError"), e); ch = -1; } // Control character or EOF (-1) terminates loop if (ch < 32 || ch == 127) { break; } command.append((char) ch); expected--; } } finally { // Close the socket now that we are done with it try { if (socket ! = null) { socket.close(); } } catch (IOException e) { // Ignore } } // Match against our command string boolean match = command.toString().equals(shutdown); if (match) { log.info(sm.getString("standardServer.shutdownViaPort")); break; } else { log.warn(sm.getString("standardServer.invalidShutdownCommand", command.toString())); } } } finally { ServerSocket serverSocket = awaitSocket; awaitThread = null; awaitSocket = null; // Close the server socket and return if (serverSocket ! = null) { try { serverSocket.close(); } catch (IOException e) { // Ignore } } } } }Copy the code

There is a volatile Boolean that controls a while loop, which continues forever unless set to false, and is used to continuously fetch “shutdown” instructions from TCP datagrams through the ServerSocket. That is, you actively turn off tomcat’s execution logic.

Fields and constructors for Thread

/* Thread name */ private volatile String name; /* private int priority; private Thread threadQ; private long eetop; /* If single_step is used to execute this thread */ private Boolean single_step; Private Boolean daemon = false; Private Boolean stillborn = false; */ private Runnable target; /* private ThreadGroup group; /* Private ClassLoader contextClassLoader; / * this thread inherits the AccessControlContext * / private AccessControlContext inheritedAccessControlContext; Private static int threadInitNumber; private static int threadInitNumber; private static synchronized int nextThreadNum() { return threadInitNumber++; } /* The ThreadLocal value associated with this thread. This map by ref; maintenance. * / ThreadLocal ThreadLocalMap threadLocals = null; /* * The InheritableThreadLocal value associated with this thread. This map is maintained by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; /* * The stack size requested by this thread, or 0 if not specified by the creator. * It depends on how the VM uses this number; Some virtual machines ignore it. */ private long stackSize; /* * The JVM private state that persists after the native thread terminates. */ private long nativeParkEventPointer; /* * thread ID */ private long tid; Private static long threadSeqNumber; private static long threadSeqNumber; Private volatile int threadStatus = 0; private volatile int threadStatus = 0; private volatile int threadStatus = 0; private static synchronized long nextThreadID() { return ++threadSeqNumber; } /** * The argument supplied to the current call to * java.util.concurrent.locks.LockSupport.park. * Set by (private) java.util.concurrent.locks.LockSupport.setBlocker * Accessed using java.util.concurrent.locks.LockSupport.getBlocker */ volatile Object parkBlocker; /* The object in which this thread is blocked in an interruptible I/O * operation, if any. The blocker's interrupt method should be invoked * after setting this thread's interrupt status. */ private volatile Interruptible blocker; private final Object blockerLock = new Object(); /* Set the blocker field; invoked via sun.misc.SharedSecrets from java.nio code */ void blockedOn(Interruptible b) { synchronized (blockerLock) { blocker = b; Public final static int MIN_PRIORITY = 1; public final static int MIN_PRIORITY = 1; /** * The priority assigned by the thread by default. */ public final static int NORM_PRIORITY = 5; Public final static int MAX_PRIORITY = 10; public final static int MAX_PRIORITY = 10; public Thread(ThreadGroup group, Runnable target, String name, long stackSize) { init(group, target, name, stackSize); }Copy the code

What is the difference between the run() and start() methods?

As you can see, Thread implements the Runnable interface by passing in the Runnable object from the constructor, so it’s handing off the task to Thread.

public void run() {
    if (target != null) {
        target.run();
    }
}
Copy the code

Causes the thread to start executing; The JVM calls the thread’s run method.

The result is two threads running at the same time: the current thread (which returns from calling the start method) and another thread (which executes its run method).

It is illegal to start a thread more than once. In particular, a thread cannot be restarted once it has finished executing. Synchronized is used to modify methods.

Public synchronized void start() {/** * This method is not called for the main method thread or "system" group thread created/set by the VM. * Any new functionality added to this method in the future may have to be added to the VM as well. * * 0 means the thread is in the "NEW" state. */ if (threadStatus ! = 0) throw new IllegalThreadStateException(); /* Notify the group that this thread is about to start so that it can be added to the group's list of threads and reduce the group's unstarted count. */ group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (! started) { group.threadStartFailed(this); }} catch (Throwable ignore) {/* do nothing. If start0 throws a throwaway, it will be passed to the call stack */}}} the local method will start a Thread to call Thread's run method. private native void start0();Copy the code

So, obviously, the run method is a normal method, which is executed by the Caller thread, just like the way you normally call an object. The start method calls a local method to create a new thread to execute the task, so two threads are said to be running simultaneously.

What is the life cycle of a thread?

We can see that the thread’s status value is threadStatus, and we can see that its assignment code is not found and should be controlled by the JVM. But we can find the get method:

public State getState() { // get current thread state return sun.misc.VM.toThreadState(threadStatus); } public static State toThreadState(int var0) { if ((var0 & 4) ! = 0) { return State.RUNNABLE; } else if ((var0 & 1024) ! = 0) { return State.BLOCKED; } else if ((var0 & 16) ! = 0) { return State.WAITING; } else if ((var0 & 32) ! = 0) { return State.TIMED_WAITING; } else if ((var0 & 2) ! = 0) { return State.TERMINATED; } else { return (var0 & 1) == 0 ? State.NEW : State.RUNNABLE; }}Copy the code

Now that we have enumerations we can look at the official definition of each state.

NEW

Threads that have not been started are in this state.

RUNNABLE

A thread executing in the JVM is in this state, but it may be waiting for other resources from the operating system, such as a processor.

BLOCKED

A thread that is prevented from waiting for a monitor lock is in this state. Like waiting for a synchronized lock.

WAITING

A thread that waits indefinitely for another thread to perform a particular operation is in this state.

The following triggers the state:

  • Object#wait() : Waits for another thread to call notify or notifyAll on this object.
  • Thread#join()
  • LockSupport#park()

TIMED_WAITING

A thread waiting for another thread to perform an operation is in this state for the specified wait time.

The following triggers the state:

  • Thread.sleep
  • Object.wait(J)
  • Thread.join(J)
  • LockSupport#parkNanos
  • LockSupport#parkUntil

TERMINATED

The exited thread is in this state.

You can experiment with the definitions, but the diagram below gives a rough idea of the transition from one state to another.

What is the difference between sleep, join and wait?

The difference between “sleep” and “wait”

  • Sleep is in the Thread class and wait is in the Object class.
  • Sleep: Setting the thread to periodic sleep causes the current thread to be in TIMED_WAITTING state; Wait is waiting for another thread to invoke notify of an object (waking up a single thread that is waiting on the monitor for that object.) Or notifyAll (wakes up all threads that are waiting on the monitor for that object.) Method, if timeout is specified, the thread is in TIMED_WAITTING state; if no timeout is specified, the thread is in WAITTING state.
  • Wait requires a synchronized lock on an object, which is thrown if not synchronizedInterruptedExceptionThe exception. The requirements are as follows:
synchronized (obj) { while (<condition does not hold>) obj.wait(); . // Perform action appropriate to condition } synchronized (obj) { ... // create condition obj.notify(); }Copy the code

Why should sleep use the same synchronized as notify?

Assume that the notify/notifyAll thread is THREAD A, and the wait thread is thread B.

In this mode, it is necessary to wait for line B to produce first and thread A to consume. If B and A happen at the same time, it is likely that A will consume before B has finished producing, and there will be no thread to consume after B has finished producing, so it needs to lock and synchronize. As for why synchronized uses obj instead of some other object, the lock is released by calling obj.wait(), and should be readded after telling wait to stop blocking.

As follows:

@SneakyThrows public static void main(String[] args) { Object lock = new Object(); Thread thread1 = new Thread(new Runnable() { @SneakyThrows @Override public void run() { synchronized (lock) { long current = System.currentTimeMillis(); System.out.println(" ready to wait "); lock.wait(); System.out.println(" wait, wait "+ (system.currentTimemillis () -current)); }}}); thread1.start(); Thread.sleep(2000); Synchronized (lock) {system.out.println (" prepare to inform "); synchronized (lock) {system.out.println (" prepare to inform "); lock.notify(); System.out.println(" Notification done "); }}Copy the code

Output:

Be ready to wait be ready to notify notify complete wait complete, wait 2006Copy the code

Verify that thread B is notified to re-lock:

@SneakyThrows public static void main(String[] args) { Object lock = new Object(); Thread thread1 = new Thread(new Runnable() { @SneakyThrows @Override public void run() { synchronized (lock) { long current = System.currentTimeMillis(); System.out.println(" ready to wait "); lock.wait(); System.out.println(" wait, wait "+ (system.currentTimemillis () -current)); Thread.sleep(2000); }}}); thread1.start(); Thread thread2 = new Thread(new Runnable() { @SneakyThrows @Override public void run() { long current = System.currentTimeMillis(); Thread.sleep(2500); Synchronized (lock) {system.out.println ("tthread2 acquired lock, execute consumption "+ (system.currentTimemillis () -current)); }}}); thread2.start(); Thread.sleep(2000); Synchronized (lock) {system.out.println (" prepare to inform "); synchronized (lock) {system.out.println (" prepare to inform "); lock.notify(); System.out.println(" Notification done "); }}Copy the code

Output:

Prepare to wait Prepare to notify notify completion The wait is complete, wait for 2013 thread2 to acquire the lock, and the execution cost 4012Copy the code

As you can see, you need to wait for Thread1 to complete before releasing the lock, so thread2 can acquire the lock.

Notify does not control whether the thread consumes notifications.

@SneakyThrows public static void main(String[] args) { Object lock = new Object(); Thread thread1 = new Thread(new Runnable() { @SneakyThrows @Override public void run() { Thread.sleep(1000); synchronized (lock) { long current = System.currentTimeMillis(); System.out.println(" ready to wait "); lock.wait(); System.out.println(" wait, wait "+ (system.currentTimemillis () -current)); }}}); thread1.start(); Synchronized (lock) {system.out.println (" prepare to inform "); synchronized (lock) {system.out.println (" prepare to inform "); lock.notify(); System.out.println(" Notification done "); }}Copy the code

Output:

Prepare notify Notify complete Prepare waitCopy the code

Has been waiting for

conclusion

Wait and notify/notifyAll are crude production and consumption modes. Notify does not care whether the wait thread has finished production. If it has not finished production, it will pass. So it’s generally not used.

Notify /notifyAll notifyAll notifyAll notifyAll notifyAll notifyAll notifyAll notifyAll notifyAll notifyAll notifyAll notify notify notifyAll notifyAll notifyAll notify notify notify notify notify notify notify notify notify notify notify notify notify notify notify notify notify notify notify notify notify notify notify notify notify notify notify notify notify notify notify notify notify notify notify notify notify notify notify notify notify notify notify Because you need to hold the lock) and return the lock.

The difference between sleep and Join

The specified number of milliseconds to sleep (temporarily halt execution) the currently executing thread based on the precision and accuracy of the system timer and scheduler. The thread does not lose any ownership of the monitor (it does not release locks like a wait does).

public static native void sleep(long millis) throws InterruptedException;
Copy the code

The time to wait for this thread to die is at most milliseconds. Timeout 0: wait forever. Use a loop condition to call this.wait internally for this.isAlive. NotifyAll is called when a thread terminates. It is recommended that applications not use Wait, notify, or notifyAll on thread instances.

Public final synchronized void join(long millis) throws InterruptedException {// Current timestamp 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

As you can see, sleep is used to sleep the current thread for a period of time, which only causes the thread to be in TIMED_WAITING. When a thread terminates, it calls notify/notifyAll. It is not used to wait for the death of its own thread, but generally used to wait for the death of another thread.

The difference between interrupt and stop

stop

Force thread execution to stop.

The thread represented by Thread will be forced to stop whatever abnormal operation it is doing and throw the newly created ThreadDeath object as an exception.

As you can see, stop is not recommended because it forces the thread to stop and the lock will not be released if it holds the lock.

@Deprecated public final void stop() { SecurityManager security = System.getSecurityManager(); // If a security manager is installed, its checkAccess method is called as an argument. This may cause a SecurityException to be raised (in the current thread). if (security ! = null) { checkAccess(); // If this thread is different from the current thread (that is, the current thread is trying to stop a thread other than itself), the security manager's checkPermission method is called separately. if (this ! = Thread.currentThread()) { security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION); }} // 0 indicates that the thread is in a NEW state, which cannot be non-new because it holds a lock. if (threadStatus ! = 0) { resume(); // Wake up the thread if it is suspended; There is no other way} // THE VM can stop threads in all states stop0(new ThreadDeath()); }Copy the code

interrupt

Interrupt the thread (just set the interrupt flag). Unless the current thread is interrupting itself (which is always allowed), the thread’s checkAccess method is called, which may cause a SecurityException to be thrown.

If the thread is blocked while calling the wait (), wait (long), or wait (long, int) methods of the object class, or the join (), Join (long), Join (long, int), sleep (long), or sleep (long, int) methods of the class, It is cleared of its interrupted status and receives InterruptedException.

public void interrupt() { if (this ! = Thread.currentThread()) checkAccess(); synchronized (blockerLock) { Interruptible b = blocker; if (b ! = null) { interrupt0(); // Just set the interrupt identifier B.innterrupt (this); return; } } interrupt0(); }Copy the code

Only threads in the RUNNABLE state can be interrupted.

Thread thread2 = new Thread(new Runnable() {
    @SneakyThrows
    @Override
    public void run() {
        while (true) {
            System.out.println("111");
        }
    }
});
thread2.start();

thread2.interrupt();
System.out.println("thread2 isInterrupted:" + thread2.isInterrupted());
Copy the code

Output:

Thread2 isInterrupted: trueCopy the code

Interrupted TIMED_WAIT:

Thread thread2 = new Thread(new Runnable() { @SneakyThrows @Override public void run() { Thread.sleep(2000); System. Out. Println (thread2 "complete"); }}); thread2.start(); thread2.interrupt(); System.out.println("thread2 isInterrupted: "+ thread2. IsInterrupted ());Copy the code
Thread2 isInterrupted: false Exception in the thread "thread - 1" Java. Lang. InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at com.study.jvm.Demo$2.run(Demo.java:73) at java.lang.Thread.run(Thread.java:748)Copy the code

conclusion

Interrput simply sends a signal. Interrupting a thread in the RUNNABLE state does not terminate. Interrupting a thread in the blocked or waiting state terminates and throws an exception.

What if an asynchronous thread wants to return results?

Go straight to code

FutureTask task = new FutureTask<Boolean>(new Callable<Boolean>() { @Override public Boolean call() throws Exception { Thread.sleep(1000); return true; }}); Thread thread = new Thread(task); thread.start(); long current = System.currentTimeMillis(); task.get(); System.out.println(" currentTimeMillis() -current) ");Copy the code

Get will block forever waiting for the result.

When do thread deadlocks occur?

Direct code simulation:

Object lock1 = new Object(); Object lock2 = new Object(); Thread thread1 = new Thread(new Runnable() { @SneakyThrows @Override public void run() { synchronized (lock1) { System.out.println("thread1 successfully holds lock1"); Thread.sleep(1000); Synchronized (lock2) {system.out.println ("thread1 successfully holds lock2"); }}}}); Thread thread2 = new Thread(new Runnable() { @SneakyThrows @Override public void run() { synchronized (lock2) { System.out.println("thread2 successfully holds lock2"); Thread.sleep(1000); Synchronized (lock1) {system.out.println ("thread2 successfully holds lock1"); }}}}); thread1.start(); thread2.start();Copy the code

Output:

Thread1 successfully holds lock1 thread2 successfully holds lock2Copy the code

I’ve been waiting.

Thread false death happens when, how to check?

Suspended animation – a thread that exists but doesn’t have any representation at all, for example:

  • No log output
  • You don’t do any homework

There is a high probability of a blocking wait or a deadlock.

You can use VisualVM, JConsole, and so on for troubleshooting. This will be described in more detail in subsequent articles.

reference

Why should WAIT be used in synchronized blocks? Why doesn’t sleep have to be in sync?