java---thread (3)---several state transitions

The state of a thread changes under certain conditions. A thread has the following states:

1. New state (New) : A thread object is newly created.

2. Ready state (Runnable) : After the thread object is created, other threads call the start () method of the object. Threads in this state are in the " runnable thread pool " and become runnable, just waiting to acquire the right to use the CPU . That is, except for the CPU , the process in the ready state has all the resources required to run.

3. Running state (Running) : The thread in the ready state acquires the CPU and executes the program code.

4. Blocked state (Blocked) : The blocked state is that the thread gives up the right to use the CPU for some reason and temporarily stops running. Until the thread enters the ready state, there is no chance to go to the running state.

There are three types of blocking:

(1) Waiting for blocking : the running thread executes the wait() method, the thread will release all the resources occupied, and the JVM will put the thread into the " waiting pool" . After entering this state, it cannot be woken up automatically. It must rely on other threads to call the notify() or notifyAll() method to wake up.

(2) Synchronization blocking : When the running thread acquires the synchronization lock of the object, if the synchronization lock is occupied by another thread, the JVM will put the thread into the "lock pool" .

(3) Other blocking : When a running thread executes the sleep() or join() method, or issues an I/O request, the JVM will place the thread in a blocked state. When the sleep() state times out, join() waits for the thread to terminate or times out, or when the I/O processing is complete, the thread re-enters the ready state.

5. Dead state (Dead) : The thread finishes executing or exits the run() method due to an exception, and the thread ends its life cycle.

The state transition diagram of thread change is as follows:


Note: Get the lock mark of the object , that is , to obtain the permission to use the object ( critical section ) . That is, the thread obtains the resources needed to run, enters the "ready state", and only needs to obtain the CPU , and it can run. Because when calling wait() , the thread will release the "lock flag" occupied by it, so the thread can only enter the ready state when it acquires resources here. The following is a small explanation: 1. There are two ways to implement threads, one is to inherit the Thread class, and the other is to implement the Runnable interface, but in any case, when we new the object, the thread enters the initial state; 2 . When the object calls the start() method, it enters the ready state; 3. After entering the ready state, when the object is selected by the operating system, it will enter the running state after obtaining the CPU time slice; 4. After entering the running state, the situation is more complicated After the     4.1 , run() method or main() method ends, the thread enters the terminated state;     4.2
 
   
 
 
 
 
, When the thread calls its own sleep() method or the join() method of other threads , the process yields the CPU , and then enters the blocking state ( this state stops the current thread, but does not release the occupied resources , that is, calls sleep () function, the thread does not release its "lock flag". ). When sleep() ends or join() ends, the thread enters the runnable state and continues to wait for the OS to allocate CPU time slices. Typically, sleep() is used to wait for a resource to be ready: after the test finds that the condition is not met, let the thread block for a period of time and retest until the condition is met.     4.3 . The thread calls the yield() method, which means to give up the currently obtained CPU time slice and return to the ready state. At this time, it is in the same competition state with other processes, and the OS may then let the process enter the running state again; call yield The effect of () is equivalent to the scheduler thinking that the thread has executed enough time slices that it needs to go to another thread. yield()
 It just makes the current thread return to the executable state, so the thread that executes yield() may be executed again immediately after entering the executable state.
   4.4
. When the thread has just entered the runnable state (note that it has not yet run), it is found that the resource to be called is synchroniza (synchronized) and the lock mark cannot be obtained. It will immediately enter the lock pool state and wait for the lock mark to be obtained ( the There may already be other threads in the lock pool waiting to acquire the lock mark. At this time, they are in the queue state, which is first come first served ). Once the thread obtains the lock mark, it will transfer to the ready state and wait for the OS to allocate a CPU time slice;

4.5. suspend() and resume() methods : The two methods are used together. suspend() makes the thread enter the blocked state and will not resume automatically. The corresponding resume() must be called to make the thread re-enter the executable state. . Typically, suspend() and resume() are used when waiting for a result produced by another thread: after the test finds that the result has not yet been produced, the thread is blocked, and after the other thread produces the result, call resume() to resume it . 4.6 wait() and notify() methods : When the thread calls the wait() method , it will enter the waiting queue ( entering this state will release all the resources it occupies, which is different from the blocking state ). After entering this state, it cannot be automatically woken up Yes , you must rely on other threads to call the notify() or notifyAll() method to be woken up (because notify() only wakes up a thread, but we cannot determine which thread is woken up, maybe the thread we need to wake up cannot be woken up. wake up, so in actual use, generally use 
  
notifyAll() method, wake up all threads), after the thread is woken up, it will enter the lock pool and wait for the lock mark to be acquired. 

wait() causes the thread to enter the blocking state, which has two forms:

One allows specifying a period of time in milliseconds as an argument; the other has no arguments. For the former, when the corresponding notify() is called or when the specified time is exceeded, the thread re-enters the executable state, that is, the ready state , while for the latter, the corresponding notify() must be called. When calling wait() , the thread will release the "lock flag" it holds , so that other synchronized data in the object where the thread is located can be used by other threads . Because waite() and notify() operate on the object's "lock flag", they must be called in a synchronized function or synchronized block . If the call is made in a non-synchronized function or a non-synchronized block , although it can be compiled and passed, an IllegalMonitorStateException exception will occur at runtime .

 

Note the difference: at first glance the wait()  and  notify()  methods are the same as the suspend() and resume() methods, but in fact they are quite different. The core of the difference is that the suspend() and all other methods described above will not release the occupied lock (if it is occupied) when the thread is blocked, while the wait()  and  notify()  pair of methods are the opposite.

The above core differences lead to a series of detailed differences

First of all, all the methods described above belong to the Thread class, but the pair of wait()  and  notify()  methods are directly affiliated to the Object class , that is, all objects have this pair of methods . At first glance, this is incredible, but in fact it is very natural, because the occupied lock needs to be released when the pair of methods blocks, and the lock is owned by any object. Calling the wait() method of any object will cause the thread to block. and the lock on that object is released. And calling the notify() method of any object causes a randomly selected one of the threads blocked by calling the object's wait() method to unblock (but not really executable until the lock is acquired).

Secondly, all the methods described above can be called from anywhere, but the pair of wait()  and  notify()  methods must be called in a synchronized method or block, and the reason is very simple, only in the synchronized method or block current Only the thread holds the lock, and the lock can be released. In the same way, the lock on the object calling this pair of methods must be owned by the current thread so that the lock can be released. Therefore, the pair of method calls must be placed in a synchronized method or block whose locked object is the one that called the pair of methods. If this condition is not met, the program can still be compiled, but an IllegalMonitorStateException exception will occur at runtime .

The above characteristics of wait() and notify() methods determine that they are often used with synchronized methods or blocks. A comparison between them and the operating system's interprocess communication mechanism will reveal their similarities: synchronized methods or blocks provide Similar to the functions of operating system primitives, their execution will not be interfered by multi-threading mechanisms, and this pair of methods is equivalent to block and wake up primitives (both of which are declared as synchronized ). Their combination allows us to implement a series of sophisticated inter-process communication algorithms (such as semaphore algorithms) on the operating system, and to solve various complex inter-thread communication problems.

Finally, two more points about the wait() and notify() methods:

First: The thread unblocked by calling the notify() method is randomly selected from the threads blocked by calling the wait() method of the object. We cannot predict which thread will be selected, so be careful when programming , to avoid problems due to this uncertainty.

Second: In addition to notify() , there is a method notifyAll() that can also play a similar role, the only difference is that calling the notifyAll() method will block all threads blocked by calling the object's wait() method at one time All unblocked. Of course, only the thread that acquired the lock can enter the executable state.

When it comes to blocking, we have to talk about deadlocks. After a little analysis, we can find that calls to the suspend() method and the wait() method without specifying a timeout period may cause deadlocks. Unfortunately, Java does not support deadlock avoidance at the language level, and we must be careful to avoid deadlocks in programming.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325598571&siteId=291194637