java并发编程------线程状态(interrupt(),isInterrupted()和interrupted())

这里写自定义目录标题

众所周知,线程是操作系统运行的最小单元,在多核心的CPU中,多条线程同时执行,达到真正意义上的并发,可以让程序的性能极大的提升。但是,多线程一直以来就是编程中的难点,如果对其的原理认识不深,极有可能造成各种各样的问题,而且非常令人费解。那么,下面我将从线程的生命周期入手,开始认识并发编程。

线程的六种状态

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:终止状态,表示当前线程执行完毕

java并发编程------线程状态(interrupt(),isInterrupted()和interrupted())

线程的启动,终止和复位

线程的启动我们用的是start方法。
线程的终止就不是调用stop方法这么简单了。stop方法在结束一个线程的时候并不会确保线程的资源正常的释放。这有可能导致不可预期的问题出现。因为这样我们并不知道当前线程还有没有用户的请求未处理,或者还有没有未处理完的请求就粗暴得停止线程。
那么要如何优雅去终止一个线程呢?
答案是:
中断在线程中主要有三个方法:interrupt(),isInterrupted()和interrupted()

  • interrupt() : 当一个线程调用另一个线程的interrupt()的时候,实际上是向该线程发出一个信号——线程中断状态已被设置,也就是告诉它:“嘿,兄弟,你可以终止线程啦。”但是这个兄弟,终不终止,什么时候终止是由这个兄弟自己决定的。
  • isInterrupted() : 用来判断当前线程的中断状态。返回true或者false。
  • interrupted() : 是个Thread的static方法,用来恢复中断状态。

下面就来简单的玩玩这三个方法~

首先: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());
    }
}

线程复位可以用来实现多个线程之间的通信。

上一篇:CountDownLatch用法与原理


下一篇:分布式锁三种解决方案