preface

In the previous article we looked at the state differences between JVM threads and kernel threads and compared the JVM RUNNABLE to the Ready and Running of kernel threads. Today we’ll focus on BLOCKED,WAITING, and TIMED_WAITING for the JVM. Although they all correspond to the same kernel state, what are the specific differences between them, or why does the JVM subdivide hibernation into three states?

Objects and Monitors

As we know, locks are often used to protect critical sections of data during Java multithreaded development. There are different types of locks depending on usage and scenario, one of which is explicit and implicit. Explicit locking requires explicit locking and unlocking in code, and implicit locking is done by the JVM. For example, the synchronized keyword corresponds to an implicit Lock, while Lock corresponds to a display Lock.

How does synchronized use locks to protect critical section data? Let’s look at some code: eight threads increment count by one at the same time. The following program ensures that we get the correct result, which is impossible without locking.

public class LifeCycleBlocked { private final static Object LOCK=new Object(); private volatile int count; private void syncMethod(){ synchronized (LOCK){ count++; ThreadUtil.println(Thread.currentThread() ,String.valueOf(count)); } } public static void main(String[] args) { String[] names={"t1","t2","t3","t4","t5","t6","t7","t8"}; LifeCycleBlocked lifeCycleBlocked =new LifeCycleBlocked(); Stream.of(names).forEach(i->{ new Thread(()-> lifeCycleBlocked.syncMethod(),i).start(); }); }}Copy the code

As you can see, synchronized requires that an object (LOCK) be declared as final. For any Java object, the JVM associates it with an entry set,wait set, and monitor.

Most articles on the Internet translate “wait set” into “wait queue”, which I personally feel is not appropriate. Because the queue structure is FIFO, the monitor is not selected in FIFO mode, depending on the JVM implementation, so I translate this as “wait set “.

There are several other uses of synchronized, but the principle is the same, so let’s use the code above as an example.

What are locks, entry sets, wait sets, and monitors, and how do they relate to each other? See below

Three threads T1, T2, and T3 all want access to the following Critical Section code

{
   count++;
   ThreadUtil.println(Thread.currentThread() ,String.valueOf(count));
}
Copy the code

So how do you ensure that only one thread accesses it? The monitor puts these three programs first into the entry set and selects one of them based on a specific algorithm, such as t1 being selected. Then, the thread ID of T1 is placed in the specified area of the LOCK object header, indicating that t1 thread has acquired the LOCK. After T1 obtains the time slice, the critical section code is executed.

If there are operations such as lock. wait() or lock. wait(long ms) in the critical section code, t1 is put into the wait set associated with the LOCK object by the monitor. T1 no longer participates in CPU thread scheduling, that is, it is no longer allocated time slices. T1 will wait until another thread calls Lock.notify or lock.notifyall (), or until a timeout is reached to notify T1. Of course, exceptions in the process of waiting will also exit the critical region.

Notification is when the monitor moves T1 from the wait set to the entry set, and then selects again. If T1 is lucky enough to be selected again, it returns from the lock. wait() method and continues to run the rest of the code until the end

If there are no operations such as lock. wait or lock. wait(long ms), t1 runs until it finishes. At the end of the run, T1 releases the lock and the thread terminates. The other threads continue to repeat the process until all threads have finished running.

Through the above process, we found that the monitor is like a steward, which controls the operation of the thread according to different operations:

  • When threads compete for locks, they are placed in the entry set, and once a thread is selected, the manager updates the ID value of that thread to the specified memory location in the object header
  • When the selected thread performs wait() or wait(long MS), the steward puts it back into the wait set. T1 will then release the lock, which will be recontested by other threads
  • When another thread calls notify() or notifyAll(), or when the specified timeout is reached, the steward moves the thread to the entry set to participate in the flip again.

For locks, it is the LOCK object in a broad sense, but in particular, it is the block of memory in the LOCK object header that holds the thread ID. Which thread ID is assigned to this area means that it owns the LOCK.

Note: The monitor does not immediately put the T1 ID into the lock, but rather has a lock upgrade process to improve performance. The details of lock escalation and the data structure of the object header will not be detailed here, but we will focus on the differences between the three states.

The difference between BLOCKED,WAITING and TIMED_WAITING can be easily distinguished by sorting out the entire process:

  • Threads in the entry collection have a BLOCKED state
  • The thread calling the wait method is in a WAITING state
  • The thread calling wait(long MS) is in TIMED_WAITING state

conclusion

By analyzing the workflow of implicit locking, we learned a few obscure concepts: locks, monitors, entry sets, and wait sets. Then through the thread flow, the difference of thread state is clarified.

Note that other operations also set threads to WAITING or TIMED_WAITING, such as thread.join (), locksupport.park (), thread.sleep (long) Ms), thread. join(long MS), locksupport. parkUntil(long deadline), locksupport. parkNanos(long nanos) will set the Thread to TIMED_WAITING. There is a Java thread state machine that summarizes all operations in detail www.uml-diagrams.org/examples/ja…

At this point, we have completed the explanation of the thread life cycle, and focused on the analysis of related states to deepen the understanding of synchronized implicit locks. If you have any questions or find any mistakes, please leave a message.