一、操作系统中线程的状态转换
在现在的操作系统中,线程是被视作为轻量级进程的,所以操作系统线程的状态和操作系统进程的状态是一致的。
二、Java线程的6个状态
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
1.NEW
处于NEW状态的线程,代表线程创建成功,但并未启动。这里的未启动,指的是还没有调用start()方法。
代码示例如下:
@Test
public void testOne(){
Thread thread = new Thread("test");
System.out.println(thread.getState());
}
//输出内容: NEW
针对start()的两个问题:
1.反复调用同一个线程的start()方法是否可行?
2.假如一个线程的状态为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) {
}
}
}
在该方法中有一个threadStatus 变量,当threadStatus不为0的时候会抛出异常终止方法的执行。
下面的 start0(); 是一个native方法,我们没有办法看到在这里面的操作。
可以通过打断点去看两次start()方法的调用threadStatus的值。
代码示例:
@Test
public void testStartMethod() {
Thread thread = new Thread(() -> {});
thread.start(); // 第一次调用
thread.start(); // 第二次调用
}
通过断点可以看到
第一次调用时threadStatus的值是0。
第二次调用时threadStatus的值不为0。
然后再来看查看线程状态的源码:
public State getState() {
// get current thread state
return sun.misc.VM.toThreadState(threadStatus);
}
--------------------------
public static State toThreadState(int var0) {
if ((var0 & 4) != 0) {
return State.RUNNABLE;
} else if ((var0 & 1024) != 0) {
return State.BLOCKED;
} else if ((var0 & 16) != 0) {
return State.WAITING;
} else if ((var0 & 32) != 0) {
return State.TIMED_WAITING;
} else if ((var0 & 2) != 0) {
return State.TERMINATED;
} else {
return (var0 & 1) == 0 ? State.NEW : State.RUNNABLE;
}
}
threadState的值为2的时候,线程的状态为TERMINATED。
所以通过分析源码得出结论:
两个 问题都是不可行的,线程在第一次调用start()方法后,变量threadState的值就会发生改变,即(threadState!=0),所以当线程再次调用start()方法的时候会抛出异常。
2.RUNNABLE
RUNNABLE状态代表线程正在执行中
我们可以通过源码注释看下Java中线程的RUNNABLE状态解释,如下所示:
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE,
由上可以看到:
处于RUNNABLE状态的线程可能在Java虚拟机中正在运行,也可能在等待其他的资源。
Java线程的RUNNABLE状态其实是包括了传统操作系统线程的ready和running两个状态的
3.BLOCKED
阻塞状态,处于阻塞状态的线程正等待锁的释放然后进入同步代码区内执行。
举个生活中的例子,A和B去银行窗口办理业务,A先办理,所以此时A先进入同步代码区域处理,
并拿到了锁。B需要等待A办理完业务离开业务窗口,释放了锁后,B才能进入同步代码区域。
4.WAITING
等待状态,处于WAITING状态的线程想要转换成RUNNABLE状态需要其他线程唤醒。
进入WAITING状态可通过如下三个方法:
Object.wait():使当前线程处于等待状态直到另一个线程唤醒它;
Thread.join():等待线程执行完毕,底层调用的是Object实例的wait方法;
LockSupport.park():除非获得调用许可,否则禁用当前线程进行线程调度。
5.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):同上,也是禁止线程进行调度指定时间;
6.TERMINATED
终止状态。表示线程已执行完毕。
三、线程状态的转换
转换图如下所示:
阻塞分为以下三种情况:
等待阻塞:通过调用线程的wait()方法,让线程等待某工作的完成。
同步阻塞:线程在获取synchronized同步锁失败(因为锁被其他线程占用),它会进入同步阻塞状态。
其他阻塞:通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或超时、或者I/O处理完毕时,线程重新转入就绪状态。
笔记总结自:https://redspider.gitbook.io/concurrent/di-yi-pian-ji-chu-pian/4