Java Thread: Transition of Thread State

Original text: https://blog.csdn.net/sinat_36042530/article/details/52565296

640?wx_fmt=jpeg

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:

640?wx_fmt=jpeg

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     4.1 . After the run () method or main() method ends, the thread enters the terminated state;     4.2 . When the thread calls its own
 
   
 
 
 
 
sleep()方法或其他线程的join()方法,进程让出CPU,然后就会进入阻塞状态(该状态既停止当前线程,但并不释放所占有的资源调用sleep ()函数后,线程不会释放它的“锁标志”。)。当sleep()结束或join()结束后,该线程进入可运行状态,继续等待OS分配CPU时间片。典型地,sleep()被用在等待某个资源就绪的情形:测试发现条件不满足后,让线程阻塞一段时间后重新测试,直到条件满足为止。
    4.3
、线程调用了yield()方法,意思是放弃当前获得的CPU时间片,回到就绪状态,这时与其他进程处于同等竞争状态,OS有可能会接着又让这个进程进入运行状态; 调用 yield() 的效果等价于调度程序认为该线程已执行了足够的时间片从而需要转到另一个线程。yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
   4.4
、当线程刚进入可运行状态(注意,还没运行),发现将要调用的资源被synchroniza(同步),获取不到锁标记,将会立即进入锁池状态,等待获取锁标记(这时的锁池里也许已经有了其他线程在等待获取锁标记,这时它们处于队列状态,既先到先得),一旦线程获得锁标记后,就转入就绪状态,等待OS分配CPU时间片;

4.5. suspend()  resume()方法:两个方法配套使用,suspend()使得线程进入阻塞状态,并且不会自动恢复,必须其对应的resume()被调用,才能使得线程重新进入可执行状态。典型地,suspend() resume() 被用在等待另一个线程产生的结果的情形:测试发现结果还没有产生后,让线程阻塞,另一个线程产生了结果后,调用 resume()使其恢复。 
   4.6
wait() notify() 方法:当线程调用wait()方法后会进入等待队列(进入这个状态会释放所占有的所有资源,与阻塞状态不同),进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()notifyAll()方法才能被唤醒(由于notify()只是唤醒一个线程,但我们由不能确定具体唤醒的是哪一个线程,也许我们需要唤醒的线程不能够被唤醒,因此在实际使用时,一般都用notifyAll()方法,唤醒有所线程),线程被唤醒后会进入锁池,等待获取锁标记。 

wait() 使得线程进入阻塞状态,它有两种形式:

一种允许指定以毫秒为单位的一段时间作为参数;另一种没有参数。前者当对应的 notify()被调用或者超出指定时间时线程重新进入可执行状态即就绪状态,后者则必须对应的 notify()被调用。当调用wait()后,线程会释放掉它所占有的“锁标志”从而使线程所在对象中的其它synchronized数据可被别的线程使用waite()notify()因为会对对象的“锁标志”进行操作,所以它们必须在synchronized函数或synchronizedblock中进行调用。如果在non-synchronized函数或non-synchronizedblock中进行调用,虽然能编译通过,但在运行时会发生IllegalMonitorStateException的异常。

 

注意区别:初看起来wait()  notify() 方法suspend() resume() 方法对没有什么分别,但是事实上它们是截然不同的。区别的核心在于,前面叙述的suspend()及其它所有方法在线程阻塞时都不会释放占用的锁(如果占用了的话),而wait()  notify() 这一对方法则相反。

上述的核心区别导致了一系列的细节上的区别

首先,前面叙述的所有方法都隶属于 Thread类,但是wait()  notify() 方法这一对却直接隶属于 Object 也就是说,所有对象都拥有这一对方法。初看起来这十分不可思议,但是实际上却是很自然的,因为这一对方法阻塞时要释放占用的锁,而锁是任何对象都具有的,调用任意对象的 wait() 方法导致线程阻塞,并且该对象上的锁被释放。而调用任意对象的notify()方法则导致因调用该对象的 wait()方法而阻塞的线程中随机选择的一个解除阻塞(但要等到获得锁后才真正可执行)。

其次,前面叙述的所有方法都可在任何位置调用,但是wait()  notify() 方法这一对方法却必须在 synchronized 方法或块中调用,理由也很简单,只有在synchronized方法或块中当前线程才占有锁,才有锁可以释放。同样的道理,调用这一对方法的对象上的锁必须为当前线程所拥有,这样才有锁可以释放。因此,这一对方法调用必须放置在这样的 synchronized方法或块中,该方法或块的上锁对象就是调用这一对方法的对象。若不满足这一条件,则程序虽然仍能编译,但在运行时会出现IllegalMonitorStateException异常。

wait()  notify()方法的上述特性决定了它们经常和synchronized方法或块一起使用,将它们和操作系统的进程间通信机制作一个比较就会发现它们的相似性:synchronized方法或块提供了类似于操作系统原语的功能,它们的执行不会受到多线程机制的干扰,而这一对方法则相当于 blockwake up 原语(这一对方法均声明为 synchronized)。它们的结合使得我们可以实现操作系统上一系列精妙的进程间通信的算法(如信号量算法),并用于解决各种复杂的线程间通信问题。

关于 wait()  notify() 方法最后再说明两点:

第一:调用notify() 方法导致解除阻塞的线程是从因调用该对象的 wait()方法而阻塞的线程中随机选取的,我们无法预料哪一个线程将会被选择,所以编程时要特别小心,避免因这种不确定性而产生问题。

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://10.200.1.11:23101/article/api/json?id=326824933&siteId=291194637