线程状态有6种,所有的线程在任何时刻必须是在这六种状态中的一种
初始(NEW):初始状态,线程被构建,但是还没有调用start()方法
运行(RUNNABLE):运行状态(就绪+运行),就绪状态的意思是指调用了start方法,在等待系统分配cpu调度的过程
阻塞(BLOCKED):阻塞状态,表示线程阻塞于锁
等待(WAITING):等待状态(进入等待状态后会释放当前的锁,然后需要等待其他线程的通知才能唤醒,唤醒进入运行状态的前提是再次得到锁)
超时等待(TIMED_WAITING):超时等待状态,该状态不同于WAITING,不一定需要其他线程通知,它可以在指定的时间内自行唤醒返回(唤醒的前提也是重新获得锁)
终止(TERMINATED):终止状态,表示当前线程已经执行完毕。
java线程状态图
只看线程的状态图好像大概知道这回事,还要结合代码看才能更容易理解
public class WaitNotify {
static boolean flag = true;
static Object lock = new Object();
public static void main(String[] args) throws Exception{
Thread waitThread = new Thread(new Wait(),"WaitThread");//(1)
waitThread.start();//(2)
TimeUnit.SECONDS.sleep(1);
Thread notifyThread = new Thread(new Notify(),"NotifyThread");//(4)
notifyThread.start();//(5)
}
static class Wait implements Runnable{
public void run(){
//加锁
synchronized (lock){
//当条件不满足时,继续wait,同时释放了lock的锁
while (flag){
try {
System.out.println(Thread.currentThread()+" flag is true. into wait @ "+
new SimpleDateFormat("HH:mm:ss").format(new Date()));
lock.wait();//(3)
}catch (InterruptedException e){
e.printStackTrace();
}
}
//当条件满足时,完成工作
System.out.println(Thread.currentThread()+" flag is false. into running @ "+
new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
}
}
static class Notify implements Runnable{
public void run(){
//加锁
synchronized (lock){
//获取lock的锁,然后进行通知,通知时不会释放lock的锁
//直到当前线程释放了lock锁,其他等待状态的线程获得了锁才会从等待状态中返回到运行状态
System.out.println(Thread.currentThread()+" hold lock. notify @ "+
new SimpleDateFormat("HH:mm:ss").format(new Date()));
lock.notifyAll();//(6)通知在等待队列中的所有线程
flag = false;//改变条件
SleepUtils.second(2);
}
//(7)
SleepUtils.second(2);
//再次加锁
synchronized(lock){//(8)
System.out.println(Thread.currentThread()+" hold lock again. sleep @ "+
new SimpleDateFormat("HH:mm:ss").format(new Date()));
SleepUtils.second(2);
}
}
}
public static class SleepUtils{
public static final void second(long seconds){
try{
TimeUnit.SECONDS.sleep(seconds);
}catch (InterruptedException e){
}
}
}
}
代码运行结果:
注:输出结果的第三行和第四行输出的顺序可能会互换,主要取决于WaitThread线程和NotifyThread线程最后那个在竞争中先获取到锁
- 当运行完标志(1)代码行的时候,线程WaitThread进入初始状态(NEW)
- 当运行完标志(2)代码行的时候,线程WaitThread进入就绪状态,等待系统的CPU调度,一旦获取CPU分配的时间片后就进入运行状态(RUNNING),就绪和运行状态统称为运行状态
- 当运行完标志(3)代码行的时候,线程WaitThread进入等待状态(WAITING),调用wait方法后,线程状态由RUNNING变为WAINING,并将当前的线程放置到对象的等待队列去。
- 当运行完标志(4)代码行的时候,线程NotifyThread进入初始状态(NEW)
- 当运行完标志(5)代码行的时候,线程NotifyThread进入就绪状态,由就绪状态变运行状态
- 当运行完标志(6)代码行的时候,notify()或notifyAll()方法调用后,原来等待状态的WaitThread线程变成阻塞状态,并不会马上从wait()处唤醒返回,需要等到notify()或notifyAll()的线程释放锁后,WaitThread线程才有机会从wait()中返回。
1.有机会从wait()返回的理解:notifyAll()的线程释放锁后,该对象中的所有等待队列的线程由等待队列移到同步队列中去,同步队列中的线程进入阻塞状态(BLOCKED),竞争该对象的锁,再次获取到锁的线程才从标志(1)的wait()处唤醒返回变成RUNNING状态
2.notify()方法是将等待队列中的一个等待线程从等待队列中移到同步队列中。
3.notifyAll()方法是将等待队列中所有的线程全部移动到同步队列中。被移动的线程由WAITING变成BLOCKED
- 当运行完标志(7)代码行的时候,NotifyThread线程释放了锁。
- 当运行完标志(8)代码行的时候,NotifyThread线程想要再次获得锁,如果得不到锁,就会由运行状态变为阻塞状态
这时候WaitThread线程已经在同步队列里,也想得到锁,就会竞争锁,如果WaitThread得到锁就会从标志(1)的wait()处唤醒继续执行后面的同步代码块,由阻塞状态变为运行状态;如果NotifyThread得到锁,就会执行标志(8)的同步代码块。