
线程状态
1.新生状态(New)
用new关键字建立一个线程对象后,该线程对象就处于新生状态。处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态。
2.就绪状态(Runnable)
处于就绪状态的线程已经具备了运行条件,但是还没有被分配到CPU,处于“线程就绪队列”,等待系统为其分配CPU。就绪状态并不是执行状态,当系统选定一个等待执行的Thread对象后,它就会进入执行状态。一旦获得CPU,线程就进入运行状态并自动调用自己的run方法。有4中原因会导致线程进入就绪状态:<br /> 1. 新建线程:调用start()方法,进入就绪状态;<br /> 2. 阻塞线程:阻塞解除,进入就绪状态;<br /> 3. 运行线程:调用yield()方法,直接进入就绪状态;<br /> 4. 运行线程:JVM将CPU资源从本线程切换到其他线程。
3.运行状态(Running)
在运行状态的线程执行自己run方法中的代码,直到调用其他方法而终止或等待某资源而阻塞或完成任务而死亡。如果在给定的时间片内没有执行结束,就会被系统给换下来回到就绪状态。也可能由于某些“导致阻塞的事件”而进入阻塞状态。
4.阻塞状态(Blocked)
阻塞指的是暂停一个线程的执行以等待某个条件发生(如某资源就绪)。有4种原因会导致阻塞:<br /> 1. 执行sleep(int millsecond)方法,使当前线程休眠,进入阻塞状态。当指定的时间到了后,线程进入就绪状态。<br /> 2. 执行wait()方法,使当前线程进入阻塞状态。当使用nofity()方法唤醒这个线程后,它进入就绪状态。<br /> 3. 线程运行时,某个操作进入阻塞状态,比如执行IO流操作(read()/write()方法本身就是阻塞的方法)。只有当引起该操作阻塞的原因消失后,线程进入就绪状态。<br /> 4. join()线程联合: 当某个线程等待另一个线程执行结束后,才能继续执行时,使用join()方法。
5.死亡状态(Terminated)
死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有两个。一个是正常运行的线程完成了它run()方法内的全部工作; 另一个是线程被强制终止,如通过执行stop()或destroy()方法来终止一个线程(注:stop()/destroy()方法已经被JDK废弃,不推荐使用)。<br /> 当一个线程进入死亡状态以后,就不能再回到其它状态了。
状态转移

- 当创建一个Thread子类或实现Runnable接口类的实例时,线程进入【新生】状态;
- 调用实例的start方法后,线程进入【就绪】状态;
- 系统会在某一时刻自动调度处于【可执行】状态的线程,被调度的线程会调用run方法,进入【运行】状态;
- 线程执行完run方法后,进入【死亡】状态;
- 处于【死亡】状态的线程,在某一时刻,会被JVM垃圾回收;
- 处于【运行】状态的线程,若调用了Thread.yield方法,会回到【就绪】状态,等待再次被调度;
- 处于【运行】状态的线程,若调用了wait方法,会进入wait set并一直等待,直到被其它线程通过notify、notifyAll、interrupt方法唤醒;
- 处于【运行】状态的线程,若调用了Thread.sleep方法,会进入【Sleep】状态,无法继续向下执行。当sleep时间结束或被interrupt时,会回到【就绪】;
- 处于【运行】状态的线程,若遇到阻塞I/O操作,也会停止等待I/O完成,然后回到【就绪】
线程调度(抢占式)
- 优先级
- wait(), sleep(), join()
wait和sleep的区别
| Wait | Sleep | |
|---|---|---|
| 作用对象 | 方法定义在Object类中, 作用于对象本身 | 定义在Thread中, 作用于当前线程 |
| 释放锁资源 | 是 | 否 |
| 使用范围 | 必须在同步代码块中 | 可以再任何地方 |
| 唤醒条件 | 其他线程调用notify()/notifyAll()方法 | 超时或者调用interrupt()方法体 |
| 方法属性 | wait是实例方法 | sleep是静态方法 |
终止线程
一般来说线程执行完毕就会结束,无需手动关闭。但是如果我们想关闭一个正在运行的线程,有什么方法呢?可以看一下Thread类中提供了一个stop()方法,调用这个方法,就可以立即将一个线程终止,非常方便。
import java.util.concurrent.*;
public class Demo2 {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread() {
@Override
public void run() {
System.out.println("start");
boolean flag = true;
while (flag) {
;
}
System.out.println("end");
}
};
thread1.setName("thread1");
thread1.start();
//当前线程休眠1秒
TimeUnit.SECONDS.sleep(1);
//关闭线程thread1
thread1.stop();
//输出线程thread1的状态
System.out.println(thread1.getState());
//当前线程休眠1秒
TimeUnit.SECONDS.sleep(1);
System.out.println(thread1.getState());
}
}
运行代码,输出:
18:02:15.312 [thread1] INFO com.itsoku.chat01.Demo01 - start18:02:16.311 [main] INFO com.itsoku.chat01.Demo01 - RUNNABLE18:02:17.313 [main] INFO com.itsoku.chat01.Demo01 - TERMINATED
代码中有个死循环,调用stop方法之后,线程thread1的状态变为TERMINATED(结束状态),线程停止了。
我们使用idea或者eclipse的时候,会发现这个方法是一个废弃的方法,也就是说,在将来,jdk可能就会移除该方法。
stop方法为何会被废弃而不推荐使用?stop方法过于暴力,强制把正在执行的方法停止了。
大家是否遇到过这样的场景:电力系统需要维修,此时咱们正在写代码,维修人员直接将电源关闭了,代码还没保存的,是不是很崩溃,这种方式就像直接调用线程的stop方法类似。线程正在运行过程中,被强制结束了,可能会导致一些意想不到的后果。可以给大家发送一个通知,告诉大家保存一下手头的工作,将电脑关闭。
线程中断
在上面中,我们已经详细讨论了stop方法停止线程的坏处,jdk中提供了更好的中断线程的方法。严格的说,线程中断并不会使线程立即退出,而是给线程发送一个通知,告知目标线程,有人希望你退出了!至于目标线程接收到通知之后如何处理,则完全由目标线程自己决定,这点很重要,如果中断后,线程立即无条件退出,我们又会到stop方法的老问题。
Thread提供了3个与线程中断有关的方法,这3个方法容易混淆,大家注意下:
public void interrupt() //中断线程public boolean isInterrupted() //判断线程是否被中断public static boolean interrupted() //判断线程是否被中断,并清除当前中断状态
interrupt()方法是一个实例方法,它通知目标线程中断,也就是设置中断标志位为true,中断标志位表示当前线程已经被中断了。isInterrupted()方法也是一个实例方法,它判断当前线程是否被中断(通过检查中断标志位)。最后一个方法interrupted()是一个静态方法,返回boolean类型,也是用来判断当前线程是否被中断,但是同时会清除当前线程的中断标志位的状态。
import java.util.concurrent.*;
public class Demo2 {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread() {
@Override
public void run() {
while (true) {
if (this.isInterrupted()) {
System.out.println("---我要退出了---");
break;
}
System.out.println("运行中");
}
}
};
thread1.setName("thread1");
thread1.start();
//当前线程休眠1秒
TimeUnit.SECONDS.sleep(1);
thread1.interrupt();
}
}
上面代码中有个死循环,interrupt()方法被调用之后,线程的中断标志将被置为true,循环体中通过检查线程的中断标志是否为ture( this.isInterrupted())来判断线程是否需要退出了。
再看一种中断的方法:
import java.util.concurrent.*;
public class Demo2 {
public static boolean isStop = false;
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread() {
@Override
public void run() {
while (true) {
if (isStop) {
System.out.println("---我要退出了---");
break;
}
System.out.println("运行中");
}
}
};
thread1.setName("thread1");
thread1.start();
//当前线程休眠1秒
TimeUnit.SECONDS.sleep(1);
isStop = true;
}
}
代码中通过一个变量isStop来控制线程是否停止。
通过变量控制和线程自带的interrupt方法来中断线程有什么区别呢?
如果一个线程调用了sleep方法,一直处于休眠状态,通过变量控制,还可以中断线程么?大家可以思考一下。
此时只能使用线程提供的interrupt方法来中断线程了。
import java.util.concurrent.*;
public class Demo2 {
public static boolean isStop = false;
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread() {
@Override
public void run() {
while (true) {
//休眠100秒
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我要退出了");
break;
}
}
};
thread1.setName("thread1");
thread1.start();
//当前线程休眠1秒
TimeUnit.SECONDS.sleep(1);
thread1.interrupt();
}
}
调用interrupt()方法之后,线程的sleep方法将会抛出 InterruptedException异常
import java.util.concurrent.*;
public class Demo2 {
public static boolean isStop = false;
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread() {
@Override
public void run() {
while (true) {
//休眠100秒
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (this.isInterrupted()) {
System.out.println("我要退出了");
break;
}
}
}
};
thread1.setName("thread1");
thread1.start();
//当前线程休眠1秒
TimeUnit.SECONDS.sleep(1);
thread1.interrupt();
}
}
运行上面的代码,发现程序无法终止。为什么?
代码需要改为:
import java.util.concurrent.*;
public class Demo2 {
public static boolean isStop = false;
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread() {
@Override
public void run() {
while (true) {
//休眠100秒
try {
TimeUnit.SECONDS.sleep(100);
} catch (InterruptedException e) {
this.interrupt();
e.printStackTrace();
}
if (this.isInterrupted()) {
System.out.println("我要退出了");
break;
}
}
}
};
thread1.setName("thread1");
thread1.start();
//当前线程休眠1秒
TimeUnit.SECONDS.sleep(1);
thread1.interrupt();
}
}
上面代码可以终止。
注意:sleep方法由于中断而抛出异常之后,线程的中断标志会被清除(置为false),所以在异常中需要执行this.interrupt()方法,将中断标志位置为true
正如中断二字所表达的意义,在线程运行(run方法)中间打断它,在Java中,提供了以下3个有关线程中断的方法
//中断线程(实例方法)
public void Thread.interrupt();
//判断线程是否被中断(实例方法)
public boolean Thread.isInterrupted();
//判断是否被中断并清除当前中断状态(静态方法)
public static boolean Thread.interrupted();
当一个线程处于被阻塞状态或者试图执行一个阻塞操作时,使用Thread.interrupt()方式中断该线程,注意此时将会抛出一个InterruptedException的异常,同时中断状态将会被复位(由中断状态改为非中断状态),如下代码将演示该过程:
public class InterruputSleepThread3 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread() {
@Override
public void run() {
//while在try中,通过异常中断就可以退出run循环
try {
while (true) {
//当前线程处于阻塞状态,异常必须捕捉处理,无法往外抛出
TimeUnit.SECONDS.sleep(2);
}
} catch (InterruptedException e) {
System.out.println("Interruted When Sleep");
boolean interrupt = this.isInterrupted();
//中断状态被复位
System.out.println("interrupt:"+interrupt);
}
}
};
t1.start();
TimeUnit.SECONDS.sleep(2);
//中断处于阻塞状态的线程
t1.interrupt();
/**
* 输出结果:
Interruted When Sleep
interrupt:false
*/
}
}
如上述代码所示,我们创建一个线程,并在线程中调用了sleep方法从而使用线程进入阻塞状态,启动线程后,调用线程实例对象的interrupt方法中断阻塞异常,并抛出InterruptedException异常,此时中断状态也将被复位。
除了阻塞中断的情景,我们还可能会遇到处于运行期且非阻塞的状态的线程,这种情况下,直接调用Thread.interrupt()中断线程是不会得到任响应的,如下代码,将无法中断非阻塞状态下的线程:
public class InterruputThread {
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(){
@Override
public void run(){
while(true){
System.out.println("未被中断");
}
}
};
t1.start();
TimeUnit.SECONDS.sleep(2);
t1.interrupt();
/**
* 输出结果(无限执行):
未被中断
未被中断
未被中断
......
*/
}
}
虽然我们调用了interrupt方法,但线程t1并未被中断,因为处于非阻塞状态的线程需要我们手动进行中断检测并结束程序,改进后代码如下:
public class InterruputThread {
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(){
@Override
public void run(){
while(true){
//判断当前线程是否被中断
if (this.isInterrupted()){
System.out.println("线程中断");
break;
}
}
System.out.println("已跳出循环,线程中断!");
}
};
t1.start();
TimeUnit.SECONDS.sleep(2);
t1.interrupt();
/**
* 输出结果:
线程中断
已跳出循环,线程中断!
*/
}
}
是的,我们在代码中使用了实例方法isInterrupted判断线程是否已被中断,如果被中断将跳出循环以此结束线程,注意非阻塞状态调用interrupt()并不会导致中断状态重置。综合所述,可以简单总结一下中断两种情况,一种是当线程处于阻塞状态或者试图执行一个阻塞操作时,我们可以使用实例方法interrupt()进行线程中断,执行中断操作后将会抛出interruptException异常(该异常必须捕捉无法向外抛出)并将中断状态复位,另外一种是当线程处于运行状态时,我们也可调用实例方法interrupt()进行线程中断,但同时必须手动判断中断状态,并编写中断线程的代码(其实就是结束run方法体的代码)。有时我们在编码时可能需要兼顾以上两种情况,那么就可以如下编写:
public void run() {
try {
//判断当前线程是否已中断,注意interrupted方法是静态的,执行后会对中断状态进行复位
while (!Thread.interrupted()) {
TimeUnit.SECONDS.sleep(2);
}
} catch (InterruptedException e) {
}
}
中断与synchronized
事实上线程的中断操作对于正在等待获取的锁对象的synchronized方法或者代码块并不起作用,也就是对于synchronized来说,如果一个线程在等待锁,那么结果只有两种,要么它获得这把锁继续执行,要么它就保存等待,即使调用中断线程的方法,也不会生效。演示代码如下:
public class SynchronizedBlocked implements Runnable{
public synchronized void f() {
System.out.println("Trying to call f()");
while(true) // Never releases lock
Thread.yield();
}
/**
* 在构造器中创建新线程并启动获取对象锁
*/
public SynchronizedBlocked() {
//该线程已持有当前实例锁
new Thread() {
public void run() {
f(); // Lock acquired by this thread
}
}.start();
}
public void run() {
//中断判断
while (true) {
if (Thread.interrupted()) {
System.out.println("中断线程!!");
break;
} else {
f();
}
}
}
public static void main(String[] args) throws InterruptedException {
SynchronizedBlocked sync = new SynchronizedBlocked();
Thread t = new Thread(sync);
//启动后调用f()方法,无法获取当前实例锁处于等待状态
t.start();
TimeUnit.SECONDS.sleep(1);
//中断线程,无法生效
t.interrupt();
}
}
我们在SynchronizedBlocked构造函数中创建一个新线程并启动获取调用f()获取到当前实例锁,由于SynchronizedBlocked自身也是线程,启动后在其run方法中也调用了f(),但由于对象锁被其他线程占用,导致t线程只能等到锁,此时我们调用了t.interrupt();但并不能中断线程。
