Java线程状态
Java线程包括以下几种状态:
- 新建状态(NEW):线程对象被创建后,就进入了新建状态。例如,Thread thread = new Thread();
- 运行状态(RUNNABLE):线程对象被创建后,其它线程调用了该对象的start()方法,从而来启动该线程。例如,thread.start(),处于就绪状态的线程,随时可以被CPU调度执行,线程一旦获得CPU时间片后就进行执行,Java线程的运行状态包括操作系统中的就绪和运行这两个状态。
- 休眠状态:休眠状态是线程因为某种原因放弃CPU的使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。休眠的情况分为三种:
- 等待状态(WAITING)
下面三种情况会出现WAITING状态:
- 调用线程的wait()方法,让线程等待某项工作的完成;
- 调用无参数的Thread.join()方法。比如线程A,当调用线程A的join()方法,调用该方法的线程会进入WAITING状态,直到线程A执行完任务,调用join()方法的线程会恢复到RUNNABLE状态;
- 调用LockSupport.park()方法,当前线程会进入WAITING状态;
- 有限等待(TIMED-WAITING)—通过调用线程的Thread.sleep()或者join(long xx)或者wait(long xx)或者LockSupport.parkUntil(longg xx)后,线程会进入有限等待状态;
- 阻塞状态(BLOCKED)—线程获取synchronized同步锁失败,它会进入同步阻塞状态,状态从RUNNABLE切换成BLOCKED。当线程获取到锁之后,状态又会从BLOCKED切换成RUNNABLE。
这里有个疑问:当线程调用阻塞式API后,线程是否会切换到BLOCKED状态?
在操作系统层面,线程会切换到休眠状态,但是在JVM层面,Java线程是不会发生变化,Java线程的状态依然是RUNNABLE状态。
- 终止状态:线程执行完毕或者因异常退出了run()方法,该线程会结束生命周期。
Java线程与操作系统线程之间的关系
Java线程和操作系统线程是两个概念,两者既有联系又有区别。首先JVM中的线程是由操作系统fork出来的,所以本质都是操作系统的线程。但是Java和操作系统均有各自的线程模型,线程的生命周期定义也有所区别。在HotSpot虚拟机中,每一个Java虚拟机线程都是直接映射到操作系统的原生线程中来实现的,中间没有额外的间接结构。
wait/notify
在Object.java中,定义了wait()、notify()和notifyAll()等方法。wait()的作用是让当前线程进入等待状态,同时,wait()也会让当前线程释放它所持有的锁,这时候共享资源也会被释放。而notify()和notifyAll()的作用,则是唤醒当前对象上的等待线程;notify()是唤醒单个线程,而notifyAll()是唤醒所有线程。
Object类中关于等待/唤醒的API详细信息如下:
- notify():唤醒在此对象监视器上等待的单个线程;
- notifyAll():唤醒在此对象监视器上等待的所有线程;
- wait():在当前线程处于阻塞状态;
- wait(long timeout):同上
- wait(long timeout,int nanos):同上
首先来看wait(long timeout)方法,它的功能是使当前线程阻塞,直到其它线程调用这个对象的notify()方法或者notifyAll()方法,或者逝去指定的时间。被阻塞的当前线程必须拥有对象的监视器。线程T(被阻塞的线程)一直处于disabled状态,直到发生下面的几个事件之一:
- 其它线程调用这个对象的notify()方法而且恰好该对象被唤醒;
- 其它线程调用这个对象的notifyAll()方法;
- 逝去指定的时间;
这里有一个特别需要强调的问题,当程序调用某个对象的wait()方法时,这时阻塞的当前线程。
class ThreadA extends Thread {
public ThreadA(String name) {
super(name);
}
@Override
public void run() {
synchronized (this) {
// (4)线程t1获取监视器,完毕后执行notify()方法,唤醒主线程
System.out.println(Thread.currentThread().getName());
notify();
}
}
}
public class Demo1 {
public static void main(String[] args) throws Exception {
Thread t1 = new ThreadA("t1");
synchronized (t1) {// (1)获取线程t1的监视器
t1.start();// (2)线程t1处于就绪状态
t1.wait();// (3)将当前线程阻塞,也就是主线程
System.out.println("主线程");//(5)主线程被唤醒
}
}
}
线程中断
线程提供了interrupt()方法,interrupt()仅仅是通知线程,线程可以执行后续的操作,同时也可以无视这个通知。被interrupt的线程,有两种方式收到通知:
- 一种是异常;
当线程A处于WAITING、TIMED_WAITING状态,如果其它线程调用线程A的interrupt()方法,会使线程A返回到RUNNABLE状态,同时线程A的代码会抛出InterruptedException异常,并将线程A的中断标识清除。
- 主动检测
如果线程处于RUNNABLE状态,并且没有阻塞。这时线程可以主动通过isInterrupted()方法检测自己是不是被中断了。
private void interrupt() {
Thread thread = Thread.currentThread();
while (true) {
//4.此时线程t的中断位被清除掉了,所以仍然会自旋。如果此处调用isInterrupted(),希望返回true,
//可以在线程t被中断后,调用5)处的thread.interrupt(),将线程t的中断位重置。
if (thread.isInterrupted()) {
break;
}
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
//3.线程响应中断后,抛出异常。打印堆栈
e.printStackTrace();
// 5.thread.interrupt();
}
System.out.println("自旋");
}
}
public static void main(String[] args) throws InterruptedException {
InterruptCaseA interruptCaseA = new InterruptCaseA();
//1.线程t启动并开始自旋
Thread t = new Thread(() -> interruptCaseA.interrupt());
t.start();
Thread.sleep(3000L);
//2.中断线程t
t.interrupt();
}