java线程的6种状态
// 线程的6种状态
public static enum State {
NEW, // 新建
RUNNABLE, // 可运行
BLOCKED, // 被阻塞
WAITING, // 等待
TIMED_WAITING, // 计时等待
TERMINATED; // 被终止
}
NEW
NEW状态的线程表示此时尚未启动,即还没调用Thread实例的start()方法
public class TestThread {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
});
System.out.println(thread.getState());
}
}
关于start()问题:
能否反复调用同一个线程的start()方法?
一个线程执行完毕(此时处于TERMINATED状态),再次调用这个线程的start()方法是否可行?
// start源码
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
这里可以看到调用了start0();点进去有一个native的start0()
方法,通过debug start方法后发现在调用一次start()之后,threadStatus的值会改变(threadStatus !=0),此时再次调用start()方法会抛出IllegalThreadStateException异常。所以,前面的两个问题都是不可以的。
RUNNABLE
RUNNABLE状态的线程表示线程正在java虚拟机中运行,也有可能在等待CPU分配资源,相当于传统操作系统的ready与runnning状态。当有多个RUNNABLE线程时,会使用最多 CPU 时间的那个(如果支持此功能)。
BLOCKED
阻塞状态。处于BLOCKED状态的线程正等待锁的释放以进入同步区。
举个例子:就好比家里的厕所,只有厕所里没人你才能进入方便(一般情况下)。
WAITING
等待状态。由WAITING->RUNNABLE需要其他线程唤醒。
线程进入等待状态的三种方法:
- Object.wait():使当前线程处于等待状态直到另一个线程唤醒它;
- Thread.join():等待线程执行完毕,底层调用的是Object实例的wait方法;
- LockSupport.park():除非获得调用许可,否则禁用当前线程进行线程调度。
举个例子:当厕所里面人出来了,你准备进去了,但是你朋友说他憋不住了他要先方便,此时你不得不释放掉锁,让你朋友先进去方便,此时你进入WAITING状态,你朋友进入RUNNABLE状态,只有你朋友进行notify()或者notifyAll(),你才能解除WAITING状态。
TIMED_WAITING
超时等待状态。线程等待一个具体的时间,时间到后会被自动唤醒。
调用如下方法会使线程进入超时等待状态:
- Thread.sleep(long millis):使当前线程睡眠指定时间;
- Object.wait(long timeout):线程休眠指定时间,等待期间可以通过notify()/notifyAll()唤醒;
- Thread.join(long millis):等待当前线程最多执行millis毫秒,如果millis为0,则会一直执行;
- LockSupport.parkNanos(long nanos): 除非获得调用许可,否则禁用当前线程进行线程调度指定时间;
- LockSupport.parkUntil(long deadline):同上,也是禁止线程进行调度指定时间;
举个例子:你到了厕所前接到一个电话让你3分钟下楼去拿个快递,你下楼等了3分钟,这3分钟你将处于TIMED_WATING,时间一过,你发现快递小哥没来,放你鸽子。你直接回去准备上厕所(争夺锁)。
TERMINATED
终止状态。表示此时线程已执行完毕。
线程状态的转换
BLOCKED与RUNNABLE状态的转换
public class TestThread {
@Test
public void blockedTest() {
Thread a = new Thread(new Runnable() {
@Override
public void run() {
testMethod();
}
},"a");
Thread b = new Thread(new Runnable() {
@Override
public void run() {
testMethod();
}
},"b");
a.start();
b.start();
System.out.println("线程名字:"+a.getName()+"线程状态:"+a.getState());
System.out.println("线程名字:"+b.getName()+"线程状态:"+b.getState());
}
//同步方法竞争锁资源
private synchronized void testMethod() {
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
执行结果:
我们可能认为程a会先调用同步方法,然后进行sleep,进入TIMED_WAITING状态,线程b则等待线程a进入BLOCKED状态
其实不然,有两点需要值得大家注意,一是在测试方法blockedTest()内还有一个main线程,二是启动线程后执行run方法还是需要消耗一定时间的。
测试方法的main线程只保证了a,b两个线程调用start()方法(转化为RUNNABLE状态),如果CPU执行效率高一点,还没等两个线程真正开始争夺锁,就已经打印此时两个线程的状态(RUNNABLE)了。
当然,如果CPU执行效率低一点,其中某个线程也是可能打印出BLOCKED状态的(此时两个线程已经开始争夺锁了)。也就是我上图的测试结果。
那么,如何打印出BLOCED状态呢?BLOCKED状态首先需要两个线程去进行锁竞争。我们可以通过处理下测试方法里的main线程,调用一下Thread.sleep()
方法。
这里需要注意的是main线程休息的时间,要保证在线程争夺锁的时间内,不要等到前一个线程锁都释放了你再去争夺锁,此时还是得不到BLOCKED状态的。
public class TestThread {
@Test
public void blockedTest() throws InterruptedException{
Thread a = new Thread(new Runnable() {
@Override
public void run() {
testMethod();
}
},"a");
Thread b = new Thread(new Runnable() {
@Override
public void run() {
testMethod();
}
},"b");
a.start();
Thread.sleep(1000L);
b.start();
System.out.println("线程名字:"+a.getName()+"线程状态:"+a.getState());
System.out.println("线程名字:"+b.getName()+"线程状态:"+b.getState());
}
//同步方法竞争锁资源
private synchronized void testMethod() {
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
在这个例子中,两线程状态如下:
- a的状态转换过程:RUNNABLE(
a.start()
) -> TIMED_WATING(Thread.sleep()
)->RUNABLE(sleep()时间到)->BLOCKED(未抢到锁) -> TERMINATED - b的状态转换过程:RUNNABLE(
b.start()
) -> BLOCKED(未抢到锁) ->TERMINATED
多次测试后,输出的结果可能会有所不同。
WAITING状态与RUNNABLE状态的转换
使线程从RUNNABLE状态转为WAITING状态有三种方法 Object.wait(); Thread.join(); LockSupport.park(),下面介绍一下常用的两种方式。
Object.wait()
调用wait()方法前线程必须持有对象锁,调用后会释放当前锁,直到有其他线程调用notify()/notifyAll()方法唤醒等待锁的线程。但有多个线程等待这个锁的时候,调用notify()不一定能够唤醒之前调用wait()那个线程,调用notifyAll()唤醒所有等待锁的线程时,时间片也不一定会把时间片分给刚才那个线程,这和系统的调度有关。
Thread.join()
调用join()方法,会一直等待这个线程执行完毕,即转换为TERMINATED。
public class TestThread {
@Test
public void blockedTest() throws InterruptedException{
Thread a = new Thread(new Runnable() {
@Override
public void run() {
testMethod();
}
},"a");
Thread b = new Thread(new Runnable() {
@Override
public void run() {
testMethod();
}
},"b");
a.start();
a.join();
b.start();
System.out.println("线程名字:"+a.getName()+"线程状态:"+a.getState());
System.out.println("线程名字:"+b.getName()+"线程状态:"+b.getState());
}
//同步方法竞争锁资源
private synchronized void testMethod() {
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
执行结果为:
其中,a线程调用start()方法后立即调用a.join(),所以main()线程会等待a线程执行完毕,至于b线程如果b线程尚未进入同步方法竞争锁资源那么将会处于RUNNABLE状态,如果进入了同步方法则会打印TIMED_WAITING状态。
TIMED_WAITING与RUNNABLE状态转换
使线程从RUNNABLE状态转为TIMED_WAITING状态有五种方法,包括Thread.sleep(long);Object.wait(long);Thread.join(long);LockSupport.parkNanos();LockSupport.parkUntil(),这里介绍三种常用的。
Thread.sleep(long)
线程进入传参毫秒休眠,释放执行权不释放锁,休眠时间一到重新进入RUNNABLE状态。
Object.wait(long)
wait(long)方法使线程进入TIMED_WAITING状态。可通过其他线程调用notify()或notifyAll()方法来唤醒。
wait(long)方法与wait()方法的区别是, wait(long)方法即使不通过其他线程调用notify()或notifyAll()方法唤醒,到达时间也会自动唤醒,去争夺锁。
Thread.join(long)
public class TestThread {
@Test
public void blockedTest() throws InterruptedException{
Thread a = new Thread(new Runnable() {
@Override
public void run() {
testMethod();
}
},"a");
Thread b = new Thread(new Runnable() {
@Override
public void run() {
testMethod();
}
},"b");
a.start();
a.join(1000L);
b.start();
System.out.println("线程名字:"+a.getName()+"线程状态:"+a.getState());
System.out.println("线程名字:"+b.getName()+"线程状态:"+b.getState());
}
//同步方法竞争锁资源
private synchronized void testMethod() {
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
执行结果为:
a线程进行join的时候小于a线程进行sleep的时间,所以a线程的状态为TIMED_WAITING
b线程状态可能为RUNNABLE或BLOCKED
线程中断
有些时候,当线程启动后并不需继续执行,需要进行线程中断,最初版本jdk中Thread类曾提供了一个线程终止的方法stop(),但是由于数据一致性问题,此暴力方法已经被废弃。目前java里还没有安全直接的方法来中断线程,但是java提供了线程中断机制来处理需要中断线程的情况。通过中断操作并不是直接的进行线程中断,而是通知需要被中断的线程自行处理。
@Deprecated(
since = "1.2"
)
// stop方法源码
public final void stop() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
this.checkAccess();
if (this != currentThread()) {
security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);
}
}
if (this.threadStatus != 0) {
this.resume();
}
this.stop0(new ThreadDeath());
}
中断方法
- Thread.interrupt():中断线程。这里的中断线程并不会立即停止线程,而是设置线程的中断状态为true(默认是flase);
- Thread.currentThread().isInterrupted():测试当前线程是否被中断。线程的中断状态受这个方法的影响,意思是调用一次使线程中断状态设置为true,连续调用两次会使得这个线程的中断状态重新转为false;
- Thread.isInterrupted():测试当前线程是否被中断。与上面方法不同的是调用这个方法并不会影响线程的中断状态。
public class InterruptedDemo {
public static void main(String[] args) {
Thread t1 = new Thread() {
@Override
public void run() {
while(true) {
System.out.println("The thread is waiting for interrupted!");
//中断处理逻辑
if(Thread.currentThread().isInterrupted()) {
System.out.println("The thread is interrupted!");
break;
}
//Thread.yield();
}
}
};
t1.start();
t1.interrupt();//中断线程
//System.out.println("The Thread is interrupted!");
}
}
在线程中断机制里,当其他线程通知需要被中断的线程后,线程中断的状态被设置为true,但是具体被要求中断的线程要怎么处理,完全由被中断线程自己而定,可以在合适的实际处理中断请求,也可以完全不处理继续执行下去。