图解JAVA线程的几个状态(JAVA笔记-线程基础篇)
Java中线程的状态,是线程在生命周期中不同时间段的状态。举个例子,我们拿小白做作业的例子比作是一条线程要执行的任务。小白掏出作业还没有开始写作业,这就说明线程准备好了。小白开始动笔写了,他在写作业了,他在奋笔疾书的写作业了,这说明线程在运行状态。小白的弟弟小黑把他笔抢去捅蚂蚁洞了,现在小白没法做作业了(他怎么就一个笔?剧情需要....),现在这条线程阻塞状态了也可能是等待状态。小白把小黑揍了一顿,抢来笔继续写作业,现在是线程又是运行状态了。几经波折,小白终于做完了作业了,线程结束。
Java中线程有几种状态
既然谈到线程,怎么离得开Thread
这个类。Java中的几个状态也都写在这个类的源码里面了。
在Thread
这个类中有一个内部枚举类型。看看源码。
public class Thread implements Runnable {
//省略n多源码...详情看源码
/**
* A thread state. A thread can be in one of the following states:
* 线程状态。一个线程的状态就下面这些了。
*
* 省略n多注释...详情看源码
*/
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
//省略n多源码...详情看源码
}
一共6种状态。但是,如果你看过很多大佬画的图,你会发现,他们画的图中,会还有一种是RUNNING的状态?(不信你去百度)但是这个JAVA只有一个RUNNABLE。怎么回事呢?请往下看。
Runnable和Running状态的区别、
为什么大部分JAVA线程状态图里都有RUNNING状态,但是JAVA源码里没有这个状态呢??
因为JAVA没法用代码把线程变成RUNNING状态,他只能让线程是准备好运行的状态RUNNABLE。
- 不正经举例:
就像妃子们没法控制自己今晚会不会被翻牌(代表RUNNING状态),她们只能准备好被翻牌,也就是RUNNABLE状态。其实CPU也不是同步运行所有线程的,每次它只能运行一个线程,就像每次皇上只能翻一个妃子的牌一样。
CPU只会不断的切换运行的线程,如果切换的足够快,看上去就像是两个线程在同步运行。
(你可以想象一个反复横跳的人,只要他跳的足够快,你看上去也就是有两个人了,好像是影分身。但其实还是只有一个人,不断的挑来跳去罢了,就像CPU不断的切换运行的线程一样。)
-
结论
running状态对于线程来说是存在的。但是java控制不了,只能把线程变成runable状态。所以源码中也就没有running了。
实在不懂,学习的时候可以把runable和running看成一个状态就好了。或者就看成7种状态。 -
注意
虽然java无法将runable状态变成runing状态,但是他可以把running状态变成runable状态。。。。。。。。。通过yield();
方法。
JAVA线程状态图
下面是要给简单的图,表示这个线程运行的很顺利,走的也很流畅。没有什么复杂的操作。没有什么很复杂的箭头。
下面来个复杂一点点的图
说明
序号 | 方法 |
---|---|
① | thread.start() |
② | 代码(方法/代码块)被synchronized修饰,线程等待获取锁。 |
③ | 线程获取了锁。 |
④ | .yield(); |
⑤ | .sleep(long); .wait(long); .join(long); LockSupport.parkNanos(long); LockSupport.parkUntil(); |
⑥ | .notify(); .notifyAll(); LockSupport.unpark(thread); |
⑦ | .wait(); .join(); LockSupport.park(); |
⑧ | .notify(); .notifyAll(); LockSupport.unpark(thread); |
⑨ | 线程里的代码运行结束 线程抛出一个没有捕获的异常或者error |
线程各个状态说明和代码实例
-
NEW
新建状态新建线程对象。public static void main(String[] args) { Thread thread=new Thread(new Runnable() { public void run() { System.out.println("我是一个新建的线程"); } }); //通过.getState();可以获取线程状态 System.out.println(thread.getState()); }
输出:NEW
-
RUNNABLE
运行.start()
将线程变成就绪状态,随时准备着被cpu翻牌。public static void main(String[] args) { Thread thread=new Thread(new Runnable() { public void run() { System.out.println("我是一个新建的线程"); } }); //start线程 thread.start(); System.out.println(thread.getState()); }
输出:
RUNNABLE
我是一个新建的线程 -
BLOCKED
线程在等待其他线程把公用的锁对象释放,所以他就一直blocked。就像小白一直等着他弟弟把笔还给他一样。自定义一个线程类,运行run()方法中要有同步代码块。然后写一个死循环,让代码一直运行,也就一直不释放锁对象。
public class MyThread extends Thread { private Object clock; //锁对象 public MyThread(Object clock){ this.clock=clock; } @Override public void run() { synchronized (clock){ //让这个线程一直运行,所以他就不会释放clock锁给其他线程用 while (true){} } } }
测试代码
public static void main(String[] args)throws Exception { Object clock=new Object(); //两个线程使用同一个锁对象 MyThread myThread1=new MyThread(clock); MyThread myThread2=new MyThread(clock); //启动两个线程 myThread1.start(); myThread2.start(); //为了不断输出线程的状态,我们在主线程里也写要给死循环。这样就能不断输出线程的状态了。 while (true){ System.out.println("myThread1状态是:"+myThread1.getState()); System.out.println("myThread2状态是:"+myThread2.getState()); //没过3秒输出一次 Thread.sleep(3*1000); } }
输出
myThread1状态是:RUNNABLE
myThread2状态是:BLOCKED
myThread1状态是:RUNNABLE
myThread2状态是:BLOCKED
myThread1状态是:RUNNABLE
myThread2状态是:BLOCKED
myThread1状态是:RUNNABLE
myThread2状态是:BLOCKED
....... -
WAITING
线程进入等待状态,只有其他线程调用⑧中的方法,才会重新进入RUNNABLE状态。public static void main(String[] args)throws Exception { Thread thread=new Thread(new Runnable() { public void run() { LockSupport.park(); } }); thread.start(); //同样的套路,把主线程循环,每3秒输出一下线程的状态 while (true){ System.out.println(thread.getState()); Thread.sleep(3*1000); } }
输出
RUNNABLE
WAITING
WAITING
WAITING
....第一个是因为运行太快了,thread线程还没来得及运行完
LockSupport.park();
主线程就把他状态输出了。 -
TIMED_WAITING
这个状态跟上面相似,不过这个状态不一定需要别的线程调用notifyAll()
这些方法来重新启动线程,在规定的时间后,此状态的线程会自动变成RUNNABLE状态。
注意是不一定需要
将上面的LockSupport.park();
改成LockSupport.parkUntil(10*1000);
。意识是10秒后自动从TIMED_WAITING变成RUNNABLE。while (true){ LockSupport.parkNanos(30*1000); }
输出
RUNNABLE
TIMED_WAITING
TIMED_WAITING
.... -
TERMINATED
就是线程运行结束了的状态。
直接上代码public void run1() throws Exception{ Thread thread=new Thread(new Runnable() { public void run() { System.out.println("线程运行结束了"); } }); thread.start(); //主线程休眠1秒,保证thread运行结束 Thread.sleep(1000); System.out.println(thread.getState()); }
输出:
线程运行结束了
TERMINATED
作者:BobC
文章原创。如你发现错误,欢迎指正,在这里先谢过了。博主的所有的文章、笔记都会在优化并整理后发布在个人公众号上,如果我的笔记对你有一定的用处的话,欢迎关注一下,我会提供更多优质的笔记的。