并发编程大师系列之:线程的定义和中断 interrupt

/ 并发编程 / 1 条评论 / 383人围观

启动线程的三种方式

继承Thread类

    public static class UseThread extends Thread { 
        public void run() {
            System.out.println("thread run 执行!");
        }
    }

启动线程

UseThread ut = new UseThread();
ut.start();

实现Runable接口

    public static class UseRun implements Runnable {
        @Override
        public void run() {
            System.out.println("runable run 执行!");
        }
    }

启动线程: 实现Runable的类不能够自己start,只能用Thread类来包装一下才可以,如:

UseRun ur = new UseRun();
new Thread(ur).start();

实现Callable接口

    public static class UseCall implements Callable<String> {
        @Override
        public String call() throws Exception {
            return "callable call 执行!";
        }
    }

它是有返回值的,它不能直接运行,需要借助FutureTask来包装,如:

UseCall uc = new UseCall();
FutureTask<String> ft = new FutureTask<>(uc);
new Tread(ft).start();
System.out.println(ft.get());

返回值需要用Future的get方法来获取。 执行结果:

callable call 执行!

使用interrupt()中断线程

首先来看一段代码:

public class InterruptTest01 {

    public static class m1 implements Runnable {

        public void run() {
            try {
                System.out.println("in run() - 睡眠20秒");
                Thread.sleep(20000);
                System.out.println("in run() - 线程唤醒");
            } catch (Exception e) {
                System.out.println("in run() - 线程睡眠中被打断了");
                // 如果没有return,线程不会实际被中断,它会继续打印下面的信息
                System.out.println("t线程sleep中被打断后中断标志位状态:" + Thread.interrupted());
                return;
            }
            System.out.println("in run() - 线程正常的消亡了");
        }
    }


    public static void main(String[] args) {
        m1 si = new m1();
        Thread t = new Thread(si);
        // 开启t线程
        t.start();
        System.out.println("调用interrupt方法前,t线程的中断状态:" + t.isInterrupted());
        try {
            // 仅仅是做标记
            t.interrupt();
            System.out.println("调用interrupt方法后,t线程的中断状态:" + t.isInterrupted());
            // 主线程休眠2秒,从而确保刚才启动的线程有机会执行一段时间
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            System.err.println("主线程catch块出问题了");
        }
        System.out.println("in main() - 主线程终止了");
    }

}

执行结果:

调用interrupt方法前,t线程的中断状态:false
调用interrupt方法后,t线程的中断状态:true
in run() - 睡眠20秒
in run() - 线程睡眠中被打断了
t线程sleep中被打断后中断标志位状态:false
in main() - 主线程终止了

分析:Thread.sleep(2000)这个方法需放在interrupt方法之后,如果放在之前,会出现中断标志位全为false的情况。如果只是单纯的调用interrupt()方法,线程并没有实际被中断,会继续往下执行。 比如:

public class InterruptTest01 {

    public static class m1 implements Runnable {

        public void run() {
            while (true){
                System.out.println("我是不会中断的了");
            }
        }
    }


    public static void main(String[] args) {
        try {
            m1 si = new m1();
            Thread t = new Thread(si);
            // 开启t线程
            t.start();
            System.out.println("调用interrupt方法前,t线程的中断状态:" + t.isInterrupted());
            // 仅仅是做标记
            t.interrupt();
            System.out.println("调用interrupt方法后,t线程的中断状态:" + t.isInterrupted());
            // 主线程休眠2秒,从而确保刚才启动的线程有机会执行一段时间
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            System.err.println("主线程catch块出问题了");
        }
        System.out.println("in main() - 主线程终止了");
    }

}

结果就是自旋,应当在run方法加上中断标志位的判断

public static class m1 implements Runnable {
        public void run() {
            while (true){
                if(Thread.interrupted()){
                    System.out.println("我竟然中断了");
                    return;
                }
                System.out.println("我是不会中断的了");
            }
        }
    }

结果:

调用interrupt方法前,t线程的中断状态:false
调用interrupt方法后,t线程的中断状态:true
我竟然中断了
in main() - 主线程终止了

待决中断

意思就是:如果线程在调用sleep()方法前被中断,那么该中断称为待决中断,它会在刚调用sleep()方法时,立即抛出InterruptedException异常。

public class PendingInterruptTest {

    public static void main(String[] args) {
        Thread.currentThread().interrupt();
        // 获取当前时间
        long startTime = System.currentTimeMillis();
        try {
            Thread.sleep(2000);
            System.out.println("主线程没有被打断");
        } catch (InterruptedException x) {
            System.out.println("主线程被打断,进入catch块");
        }
        // 计算中间代码执行的时间
        System.out.println("时间差 =" + (System.currentTimeMillis() - startTime));
    }

}

结果:

主线程被打断,进入catch块
时间差 =1

interrupted() 和 isInterrupted() 的区别?

首先看一段代码

public class InterruptTest01 {

    public static class m1 implements Runnable {
        public void run() {
            System.out.println("我是不会中断的了");
        }
    }


    public static void main(String[] args) {
        try {
            m1 si = new m1();
            Thread t = new Thread(si);
            // 开启t线程
            t.start();
            System.out.println("调用interrupt方法前0,t线程的中断状态:" + t.interrupted());
            t.interrupt();
            System.out.println("调用interrupt方法后1,t线程的中断状态:" + t.interrupted());
            System.out.println("调用interrupt方法后2,t线程的中断状态:" + t.interrupted());
            // 主线程休眠2秒,从而确保刚才启动的线程有机会执行一段时间
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            System.err.println("主线程catch块出问题了");
        }
        System.out.println("in main() - 主线程终止了");
    }

}

结果:

调用interrupt方法前0,t线程的中断状态:false
调用interrupt方法后1,t线程的中断状态:false
调用interrupt方法后2,t线程的中断状态:false
我是不会中断的了
in main() - 主线程终止了

分析:结果竟然都是false,可见thread 线程并没有停止,而且调用 thread.interrupted() 结果是两个 false 表示线程一直在运行过程中。官方解释:当前线程是指运行 this.interrupted() 方法的线程 。也就是说,当前线程并不是 t,并不是因为 t 调用了 interrupted() 方法就是当前线程。当前线程一直是 main 线程,它从未中断过,所以打印结果就是两个 false。

改变:

public class InterruptTest01 {

    public static class m1 implements Runnable {
        public void run() {
            System.out.println("我是不会中断的了");
        }
    }


    public static void main(String[] args) {
        try {
            m1 si = new m1();
            Thread t = new Thread(si);
            // 开启t线程
            t.start();
            System.out.println("调用interrupt方法前0,t线程的中断状态:" + Thread.interrupted());
            Thread.currentThread().interrupt();
            System.out.println("调用interrupt方法后1,t线程的中断状态:" + Thread.interrupted());
            System.out.println("调用interrupt方法后2,t线程的中断状态:" + Thread.interrupted());
            // 主线程休眠2秒,从而确保刚才启动的线程有机会执行一段时间
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            System.err.println("主线程catch块出问题了");
        }
        System.out.println("in main() - 主线程终止了");
    }

}

结果:

调用interrupt方法前0,t线程的中断状态:false
调用interrupt方法后1,t线程的中断状态:true
调用interrupt方法后2,t线程的中断状态:false
我是不会中断的了
in main() - 主线程终止了

从上述结果中可以看出,方法 interrupted() 的确判断出当前线程是否已经停止,但是为什么第 2 个布尔值是 false 呢?文档中说的很详细,interrupted() 方法具有清除状态的功能,所以第二次的时候返回值是 false。


isInterrupted()方法代码

public static void main(String[] args) {
        try {
            Thread t = Thread.currentThread();
            System.out.println("调用interrupt方法前0,t线程的中断状态:" + t.isInterrupted());
            t.interrupt();
            System.out.println("调用interrupt方法后1,t线程的中断状态:" + t.isInterrupted());
            System.out.println("调用interrupt方法后2,t线程的中断状态:" + t.isInterrupted());
            // 主线程休眠2秒,从而确保刚才启动的线程有机会执行一段时间
            Thread.sleep(2000);
            System.out.println("还在输出吗?");
        } catch (InterruptedException e) {
            System.err.println("主线程catch块出问题了");
            System.out.println("进入主线程catch块中,t线程的中断状态:" + t.isInterrupted());
        }
        System.out.println("in main() - 主线程终止了");
    }

结果:

调用interrupt方法前0,t线程的中断状态:false
调用interrupt方法后1,t线程的中断状态:true
调用interrupt方法后2,t线程的中断状态:true
进入主线程catch块中,t线程的中断状态:false
in main() - 主线程终止了
主线程catch块出问题了

线程一旦被中断,isInterrupted()方法便会返回true,而一旦sleep()方法抛出异常,它将清空中断标志,此时isInterrupted()方法将返回false。如果此方法是一个循环方法,那么需要手动将中断标志位置为true,比如在调用一次interrupt()方法。

总结:interrupted():测试 当前线程 是否已经是中断状态,执行后具有清除状态功能。isInterrupted():测试线程 Thread 对象 是否已经是中断状态,但不清除状态标志。

  1. 不错不错不错

    回复