线程的状态转换:
1、新建状态
用new Thread()建立一个线程对象后,该线程对象就处于新生状态。
2、就绪状态
通过调用线程的start方法进入就绪状态(runnable)。注意:不能对已经启动的线程再次调用start()方法,否则会出现Java.lang.IllegalThreadStateException异常。
处于就绪状态的线程已经具备了运行条件,但还没有分配到CPU,处于线程就绪队列(就绪池),等待系统为其分配CPU。
Note:如果希望子线程调用start()方法后立即执行,可以使用Thread.sleep()方式使主线程睡眠一伙儿,转去执行子线程。
3、运行状态
处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
处于就绪状态的线程,如果获得了cpu的调度,就会从就绪状态变为运行状态,执行run()方法中的任务。
如果该线程失去了cpu资源,就会又从运行状态变为就绪状态,重新等待系统分配资源。
也可以对在运行状态的线程调用yield()方法,它就会让出cpu资源,再次变为就绪状态。
4、阻塞状态
当发生如下情况时,线程会让出CPU控制权并暂时停止自己的运行,从运行状态变为阻塞状态:
① 线程调用sleep方法主动释放CPU控制权
② 线程调用一个阻塞式IO方法,在该方法返回之前,该线程被阻塞
③ 线程试图获得一个同步监视器,但更改同步监视器正被其他线程所持有
④ 线程在等待某个通知(notify)
⑤ 程序调用了线程的suspend方法将线程挂起。不过该方法容易导致死锁,所以程序应该尽量避免使用该方法。
在阻塞状态的线程不能进入就绪队列。只有当引起阻塞的原因消除时,如睡眠时间已到,或等待的I/O设备空闲下来,线程便转入就绪状态,重新到就绪队列中排队等待,被系统选中后从原来停止的位置开始继续运行。
当发生如下情况时,线程会从运行状态变为阻塞状态:
5、死亡状态
当线程的run()方法执行完,或者被强制性地终止,例如出现异常,或者调用了stop()、desyory()方法等等,就会从运行状态转变为死亡状态。
线程一旦死亡,就不能复生。如果在一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。
Note:
1. sleep() 是Thread的静态方法,最好不要用Thread的实例对象调用它,因为它睡眠的始终是当前正在运行的线程,而不是调用它的线程对象,它只对正在运行状态的线程对象有效。
2. Java线程调度是Java多线程的核心,只有良好的调度,才能充分发挥系统的性能,提高程序的执行效率。
但是不管程序员怎么编写调度,只能最大限度的影响线程执行的次序,而不能做到精准控制。
使用sleep方法之后,线程是进入阻塞状态的,只有当睡眠的时间结束,才会重新进入到就绪状态,而就绪状态进入到运行状态,是由系统控制的,我们不可能精准的去干涉它,所以如果调用Thread.sleep(1000)使得线程睡眠1秒,可能结果会大于1秒。
3. yield()方法
yield()方法和sleep()方法有点相似,也是Thread类提供的一个静态的方法,它也可以让当前正在执行的线程暂停,让出cpu资源给其他的线程。
但是和sleep()方法不同的是,它不会进入到阻塞状态,而是进入到就绪状态。
4. join()方法,线程合并
线程的合并的含义就是将几个并行线程的线程合并为一个单线程执行,应用场景是当一个线程必须等待另一个线程执行完毕才能执行时,Thread类提供了join方法来完成这个功能,注意,它不是静态方法。
从上面的方法的列表可以看到,它有3个重载的方法:
void join()
当前线程等待加入该线程后面,等待该线程终止。即,等待该线程执行完毕后再执行当前线程。
void join(long millis)
当前线程等待该线程终止的时间最长为 millis 毫秒。 如果在millis时间内,该线程没有执行完,那么当前线程进入就绪状态,重新等待cpu调度
void join(long millis,int nanos)
等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。如果在millis时间内,该线程没有执行完,那么当前线程进入就绪状态,重新等待cpu调度
5. 守护线程
守护线程与普通线程写法上基本么啥区别,调用线程对象的方法setDaemon(true),则可以将其设置为守护线程。
setDaemon方法的详细说明:
public final void setDaemon(boolean on)将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。该方法必须在启动线程前调用。
实际上:JRE判断程序是否执行结束的标准是所有的前台执线程行完毕了,而不管后台线程的状态,因此,在使用后台线程(守护线程)时候一定要注意这个问题。
守护线的好处就是你不需要关心它的结束问题。例如你在你的应用程序运行的时候希望播放背景音乐,如果将这个播放背景音乐的线程设定为非守护线程,那么在用户请求退出的时候,
不仅要退出主线程,还要通知播放背景音乐的线程退出;如果设定为守护线程则不需要了。
6. 如何结束一个线程
Thread.stop()、Thread.suspend、Thread.resume、Runtime.runFinalizersOnExit这些终止线程运行的方法已经被废弃了,使用它们是极端不安全的!
想要安全有效的结束一个线程,只要保证在一定的情况下,run方法能够执行完毕即可。而不是while(true)的无线循环。
有些时候线程的run()方法不能正常的执行完毕(可能在运行时转成了阻塞),这种情况下可以借助Thread对象的interrupt()方法将中断状态设置为true来结束一个线程。
当一个线程处于sleep、wait、join这三种状态之一的时候,如果此时他的中断状态为true,那么它就会抛出一个InterruptedException的异常,并将中断状态重新设置为false。
我们看看sleep、wait、join方法的声明:
public final void wait() throws InterruptedException public static native void sleep(long millis) throws InterruptedException public final void join() throws InterruptedException
可以看到,这三者有一个共同点,都抛出了一个InterruptedException的异常。所以,我们可以使用interrupt这个巧妙的方式结束掉这种情况下的线程。
原文地址:https://www.cnblogs.com/lcj12121/p/11370282.html