The five states of a thread
Threads are generally divided into five states from creation to destruction, as shown below:
1) new
When a thread is created with the new keyword, it is in the new state.
2) ready
After the start method is called, the thread enters the ready phase. At this point, the thread does not execute the run method immediately and needs to wait for CPU resources.
3) run
When the thread gets the CPU time slice, it enters the running state and executes the run method.
4) block
A thread moves from a running state to a blocked state when it encounters one of the following conditions.
- Call the sleep method to put the thread to sleep.
- Call the wait method to make the thread wait.
- When a thread acquires a synchronous lock, the lock is being held by another thread.
- A thread blocks when a blocking IO method is called.
- Calling the suspend method, which suspends the thread, also blocks.
Note that the blocked state can only enter the ready state, not the running state directly. This is because switching from the ready state to the run state is not controlled by the thread itself, but by the thread scheduler. It is only after the thread has acquired the CPU time slice that it enters the running state.
5) death
The thread dies when the run method ends normally, or when an exception is thrown for some reason. In addition, calling the stop method directly will also stop the thread. However, this method has been deprecated and is not recommended.
Common thread methods
1) sleep.
When thread.sleep (Long millis) sleep is called, the current Thread is blocked. The millis parameter specifies how long the thread sleeps in milliseconds. When the time expires, the thread reenters the ready state.
Note that if the current thread acquires a synchronous lock, the lock is not released while the sleep method is blocking.
2) Wait, notify and notifyAll
First, they are all methods in the Object class. Synchronized is used with the keyword.
Calling a thread’s wait method causes the current thread to wait until another thread calls notify/notifyAll of the object. If N threads are waiting for the lock, notify wakes up one of them randomly, and notifyAll wakes up all threads in the lock. Note that the lock is not released immediately upon awakening, but only after the current thread completes execution.
In addition, the wait method differs from the sleep method in that the sleep method does not release the lock while the wait method does. Wait and notify can be used as follows:
public class WaitTest {
private static Object obj = new Object();
public static void main(String[] args) throws InterruptedException {
ListAdd listAdd = new ListAdd();
Thread t1 = new Thread(() -> {
synchronized (obj){
try {
for (int i = 0; i < 10; i++) {
listAdd.add();
System.out.println("Current thread :"+Thread.currentThread().getName()+"Added an element.");
Thread.sleep(300);
if(listAdd.getSize() == 5){
System.out.println("Give notice"); obj.notify(); } } } catch(InterruptedException e){ e.printStackTrace(); }}}); Thread t2 = new Thread(() -> { synchronized (obj){ try {if(listAdd.getSize() ! = 5){ obj.wait(); } } catch(InterruptedException e){ e.printStackTrace(); } System.out.println("Thread."+Thread.currentThread().getName()+"Informed."); }}); t2.start(); Thread.sleep(1000); t1.start(); } } class ListAdd { private static volatile List<String> list = new ArrayList<String>(); public voidadd() {
list.add("abc");
}
public int getSize() {
returnlist.size(); }}Copy the code
Create a t2 thread and check if the list length is 5. If not, block. Then, another T1 thread keeps adding elements to the list and wakes up the blocking T2 thread when the element length is 5.
However, we see that the T1 thread continues to execute. The lock is not released until the method completes execution. When the T1 thread wakes up T2, it simply makes T2 eligible to compete for the lock. T2 will proceed only after it has actually acquired the lock.
3) the join
When a thread calls another thread’s join method, the current thread blocks. The current thread does not switch from blocked to ready until another thread completes execution.
You might be asked, in an interview, how do you make sure that the T1, T2, and T3 threads execute in sequence. (As we know, you can’t control the order of execution of threads after calling start.)
Cough cough, the current green I, interview was asked this question, is a face meng force. Join (); join ();
public class TestJoin {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new MultiT("a"));
Thread t2 = new Thread(new MultiT("b"));
Thread t3 = new Thread(new MultiT("c"));
t1.start();
t1.join();
t2.start();
t2.join();
t3.start();
t3.join();
}
}
class MultiT implements Runnable{
private String s;
private int i;
public MultiT(String s){
this.s = s;
}
@Override
public void run() {
while(i<10){
System.out.println(s+"= = = ="+i++); }}}Copy the code
Eventually, we’ll see that threads are executed in t1, T2, and T3 order. The main thread always waits until the thread that called the JOIN method has finished executing.
4) yield
The thread. yield method causes the current Thread to yield its CPU time slice to a Thread of the same or higher priority.
Notice that the current thread is not blocking, but is in a ready state, ready to pick up the CPU time slice again and run. In other words, yield does not guarantee that another thread of equal or higher priority will get the right to execute, or that the current thread will get the right to execute again.
The yield method, like the sleep method, does not release the lock resource. You can verify this with code:
public class TestYield {
public static void main(String[] args) {
YieldThread yieldThread = new YieldThread();
for (int i = 0; i < 10; i++) {
Thread t = new Thread(yieldThread);
t.start();
}
}
}
class YieldThread implements Runnable {
private int count = 0;
@Override
public synchronized void run() {
for (int i = 0; i < 10; i++) {
count ++;
if(count == 1){
Thread.yield();
System.out.println("Thread."+Thread.currentThread().getName() + "Concession");
}
System.out.println("Thread."+Thread.currentThread().getName() + ",count:"+count); }}}Copy the code
Results:
You will see that the lock is not released after the thread gives in. Therefore, no other thread has a chance to acquire the lock, but only after the current thread has finished executing, will it be released. (I actually have my doubts about that. Yield doesn’t release the lock, so why give up execution? Even if you give up execution, no other thread can acquire the lock.
So, as I understand it, yield is generally used in multithreaded environments where lock contention does not exist. If the current thread is likely to be performing tasks for a long time, you can use yield to temporarily yield CPU execution. Give other threads a chance to perform tasks without draining CPU resources on the current thread.
Suspend, resume 5
Suspend suspends a thread and does not automatically resume. Only a call to the resume method can put the thread into a ready state. Note that these two methods have been deprecated due to potential deadlocks.