java线程一共有6种状态:
NEW:初始状态:线程被构建,但是还没有调用start方法
RUNNABLED:运行状态,JAVA线程把就绪和运行两种状态统一称为“运行中”(线程在启动的时候不是立马运行的,而是要通过os调度才会去执行)
BLOCKED:阻塞状态,表示线程进入等待状态,也就是线程因为某种原因放弃了CPU使用权,阻塞也分为几种情况
等待阻塞:运行的线程执行wait方法,jvm会把当前线程放入到等待队列。
同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被其他线程锁占用了,那么jvm会把当前的线程放入到锁池中。
其他阻塞:运行的线程执行Thread.sleep或者t.join方法,或者发出了I/O请求时,JVM会把当前线程设置为阻塞状态,当sleep结束、join线程终止、io处理完毕则线程恢复。
TIME_WAITING:超时等待状态,超时以后自动返回
TERMINATED:终止状态,表示当前线程执行完毕
线程的启动我们用的是start方法。
线程的终止就不是调用stop方法这么简单了。stop方法在结束一个线程的时候并不会确保线程的资源正常的释放。这有可能导致不可预期的问题出现。因为这样我们并不知道当前线程还有没有用户的请求未处理,或者还有没有未处理完的请求就粗暴得停止线程。
那么要如何优雅去终止一个线程呢?
答案是:
中断在线程中主要有三个方法:interrupt(),isInterrupted()和interrupted()
下面就来简单的玩玩这三个方法~
首先:interrupt()
public class InterruptTest{
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
while (true){
if(Thread.currentThread().isInterrupted()){
System.out.println("线程已经被interrupt()方法作用,但是线程是否中断还未可知");
}else{
System.out.println("线程还没有被interrupt()方法作用");
}
}
},"InterruptTest");
thread.start();
Thread.sleep(1000);
//TimeUnit.SECONDS.sleep(1);
thread.interrupt();
System.out.println("主线程结束.....");
}
}
从结果中我们可以看到,线程虽然中断。但是仍然一直不断的运行。
那么如何正确的中断线程呢?
可以通过添加一个volatile boolean isStop = false;这样的标识。这是因为,volatile能够实现多线程之间共享变量的可见性这一特点 。当其他线程在修改这个值的时候,由于volatile 保证了可见性,那么程序看到这个变化以后就会让其停止。
public class InterruptTest{
private volatile static boolean isStop = false;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
while (!isStop){
if(Thread.currentThread().isInterrupted()){
System.out.println("线程已经被interrupt()方法作用,但是线程是否中断还未可知");
}else{
System.out.println("线程还没有被interrupt()方法作用");
}
}
},"interruptTest");
thread.start();
Thread.sleep(1000);
ThreadTest.isStop = true;
System.out.println("主线程结束.....");
}
}
但是这个方法看似解决了问题,但是其实当遇到线程阻塞的时候,这个方法就无可奈何了。
public class InterruptTest{
private volatile static boolean isStop = false;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
while (!isStop){
try {
Thread.sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"interruptTest");
thread.start();
Thread.sleep(1000);
ThreadTest.isStop = true;
System.out.println("主线程结束.....");
}
}
我们可以看到,虽然已经将中断标识设置为true了,但是线程却一直阻塞这,并未真正中断。
加入interrupt()方法可以有效解决这个问题。
interrupt()它可以迅速中断被阻塞的线程,抛出一个InterruptedException,把线程从阻塞状态中解救出来。
public class InterruptTest{
private volatile static boolean isStop = false;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
while (!isStop){
try {
Thread.sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"interruptTest");
thread.start();
Thread.sleep(1000);
ThreadTest.isStop = true;
thread.interrupt();
System.out.println("主线程结束.....");
}
}
通过这样的方式可以让线程在终止的时候有机会清理资源,这样的方式去更优雅安全。
接下来说说interrupted()
interrupted()可以对设置了中断标识的线程进行复位。比如A线程调用了interrupt来设置B线程中断,而在B线程内通过interrupted()对标识复位。这样,线程并没有停止。
public class InterruptedTest{
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
while (true){
boolean isStop = Thread.currentThread().isInterrupted();
if(isStop){
// 如果线程被中断过的话 isStop会变成true
System.out.println("before:"+in);
//设置复位,就变成 false
Thread.interrupted();
System.out.println("after:"+Thread.currentThread().isInterrupted());
}else{
System.out.println("线程还没有被interrupt()方法作用");
}
}
},"InterruptedTest");
thread.start();
Thread.sleep(1000);
thread.interrupt();
System.out.println("主线程结束.....");
}
}
其他的线程复位
上面为解决volatile标识无法应对阻塞线程的情况一样,引入interrupt()方法。它会抛出一个InterruptedException异常把线程从阻塞中解救出来。这是一种被动的复位的方法。在InterruptedException抛出之前,JVM会先把线程的中断标识位清除,然后才会抛出InterruptedException,这个时候如果调用isInterrupted方法,将会返回false。
public class InterruptedTest {
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(()->{
while(true){
// 抛异常会复位
try {
Thread.sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
TimeUnit.SECONDS.sleep(1);
// 设置复位表示为true
thread.interrupt();
TimeUnit.SECONDS.sleep(1);
// 输出false
System.out.println(thread.isInterrupted());
}
}
线程复位可以用来实现多个线程之间的通信。