JDK Thread interrupt 中断

博文目录


JDK Thread join

线程中断

通过 stop 来终止线程太过粗鲁, 且不安全, 可能有某些资源没有关闭, 锁没有释放等造成问题

通过 interrupt 给线程设置中断标记, 是否响应中断, 由线程自己来决定, 可以更温和地在线程安全点终止线程

可以通过线程中断标记, 在一定程度上做到线程通信

中断说明

thread.interrupt(), 给指定线程设置中断标记
thread.isInterrupted(), 获取指定线程的中断标记
Thread.interrupted(), 获取指定线程的中断标记, 且有标记则清除

如果线程执行 Object.wait() / Thread.sleep() / Thread.join() 前被设置了中断标记, 则执行到这些代码时, 不会睡眠或阻塞, 直接抛出中断异常, 并清除中断标记

如果线程正在执行 Object.wait() / Thread.sleep() / Thread.join() 中时被设置了中断标记, 则直接唤醒线程, 抛出中断异常, 并清除中断标记

代码验证

@Test
@SneakyThrows
public void test() {
    
    

    // thread.isInterrupted(), 获取中断标记
    // thread.interrupt(), 设置中断标记, 但不会改变线程的运行状态
    // Thread.interrupted(), 获取中断标记, 有则清除
    // Thread.sleep(), 分两种情况, 1.线程sleep前被设置中断,执行到sleep时,不睡,直接抛出中断异常,并清除中断标记, 2.线程sleep中被设置中断,直接唤醒线程,抛出中断异常,并清除中断标记
    // object.wait(), 分两种情况, 1.线程wait前被设置中断,执行到wait时,不阻塞,直接抛出中断异常,并清除中断标记, 2.线程wait中被设置中断,直接唤醒线程,抛出中断异常,并清除中断标记
    // Thread.join(), 分两种情况, 1.线程join前被设置中断,执行到join时,不阻塞,直接抛出中断异常,并清除中断标记(本质上是wait), 2.线程join中被设置中断,直接唤醒线程,抛出中断异常,并清除中断标记(本质上是wait)

    Thread thread = new Thread(() -> {
    
    
        Thread me = Thread.currentThread();

        log.info("默认是没有中断标记的: {}", me.isInterrupted());


        System.out.println();
        // 给线程设置中断标记
        me.interrupt();
        log.info("设置中断标记后: {}", me.isInterrupted());


        System.out.println();
        // 设置中断标记
        me.interrupt();
        // 获取线程中断标记, 会清除中断标记
        // 内部是 Thread.currentThread().isInterrupted(true);
        log.info("调用 Thread.interrupted() 返回的值: {}", Thread.interrupted());
        log.info("调用 Thread.interrupted() 会清除中断标记: {}", me.isInterrupted());
        // 清除中断标记
        Thread.interrupted();


        System.out.println();
        // 设置中断标记
        me.interrupt();
        // 主动抛出中断异常, 是不会清除线程的中断标记的
        try {
    
    
            throw new InterruptedException();
        } catch (InterruptedException e) {
    
    
            log.info("主动抛出中断异常, 不会清除中断标记: {}", me.isInterrupted());
        }
        // 清除中断标记
        Thread.interrupted();


        System.out.println();
        // 设置中断标记
        me.interrupt();
        // 线程sleep前被设置中断,执行到sleep时,不睡,直接抛出中断异常,并清除中断标记
        try {
    
    
            log.info("子线程开始 sleep:10 秒");
            // 本质上调用的是 Thread.sleep()
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
    
    
            log.info("线程sleep前被设置中断,执行到sleep时,不睡,直接抛出中断异常,并清除中断标记: {}", me.isInterrupted());
        }
        // 清除中断标记
        Thread.interrupted();


        System.out.println();
        // 线程sleep中被设置中断,直接唤醒线程,抛出中断异常,并清除中断标记
        try {
    
    
            log.info("子线程开始 sleep:10 秒");
            // 本质上调用的是 Thread.sleep()
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
    
    
            log.info("线程sleep中被设置中断,直接唤醒线程,抛出中断异常,并清除中断标记: {}", me.isInterrupted());
        }
        // 清除中断标记
        Thread.interrupted();


        System.out.println();
        // 设置中断标记
        me.interrupt();
        // 线程wait前被设置中断,执行到wait时,不阻塞,直接抛出中断异常,并清除中断标记
        synchronized (this) {
    
    
            try {
    
    
                log.info("子线程开始 wait:10 秒");
                wait(1000 *10);
            } catch (InterruptedException e) {
    
    
                log.info("线程wait前被设置中断,执行到wait时,不阻塞,直接抛出中断异常,并清除中断标记: {}", me.isInterrupted());
            }
        }
        // 清除中断标记
        Thread.interrupted();


        System.out.println();
        // 线程wait前被设置中断,执行到wait时,不阻塞,直接抛出中断异常,并清除中断标记
        synchronized (this) {
    
    
            try {
    
    
                log.info("子线程开始 wait:10 秒");
                wait(1000 *10);
            } catch (InterruptedException e) {
    
    
                log.info("线程wait中被设置中断,直接唤醒线程,抛出中断异常,并清除中断标记: {}", me.isInterrupted());
            }
        }
        // 清除中断标记
        Thread.interrupted();


        System.out.println();
        // 子线程里再开一个需要执行10秒的新子线程
        Thread other = new Thread(() -> {
    
    
            try {
    
    
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
        });
        // 启动该新子线程
        other.start();
        // 设置中断标记
        me.interrupt();
        // 线程join前被设置中断,执行到join时,不阻塞,直接抛出中断异常,并清除中断标记(本质上是wait)
        try {
    
    
            // 让子线程me等待子线程other之行结束
            log.info("子线程开始 join:10 秒");
            other.join(1000 * 10);
        } catch (InterruptedException e) {
    
    
            log.info("线程join前被设置中断,执行到join时,不阻塞,直接抛出中断异常,并清除中断标记(本质上是wait): {}", me.isInterrupted());
        }
        // 清除中断标记
        Thread.interrupted();


        System.out.println();
        // 线程join中被设置中断,直接唤醒线程,抛出中断异常,并清除中断标记(本质上是wait)
        try {
    
    
            // 让子线程me等待子线程other之行结束
            log.info("子线程开始 join:10 秒");
            other.join(1000 * 10);
        } catch (InterruptedException e) {
    
    
            log.info("线程join中被设置中断,直接唤醒线程,抛出中断异常,并清除中断标记(本质上是wait): {}", me.isInterrupted());
        }
        // 清除中断标记
        Thread.interrupted();


        System.out.println();
        log.info("子线程结束");
        System.out.println();
    });
    thread.start();

    TimeUnit.SECONDS.sleep(1);
    log.info("主线程睡1秒后给子线程设置中断标记, 测试sleep中被设置中断");
    thread.interrupt();

    TimeUnit.SECONDS.sleep(1);
    log.info("主线程再睡1秒后给子线程设置中断标记, 测试wait中被设置中断");
    thread.interrupt();

    TimeUnit.SECONDS.sleep(1);
    log.info("主线程再睡1秒后给子线程设置中断标记, 测试join中被设置中断");
    thread.interrupt();

    TimeUnit.SECONDS.sleep(1);
    log.info("主线程再睡1秒等待子线程测试完成");

    log.info("主线程结束");

}
[20220518.173753.647][INFO ][Thread-1] 默认是没有中断标记的: false

[20220518.173753.650][INFO ][Thread-1] 设置中断标记后: true

[20220518.173753.650][INFO ][Thread-1] 调用 Thread.interrupted() 返回的值: true
[20220518.173753.650][INFO ][Thread-1] 调用 Thread.interrupted() 会清除中断标记: false

[20220518.173753.650][INFO ][Thread-1] 主动抛出中断异常, 不会清除中断标记: true

[20220518.173753.651][INFO ][Thread-1] 子线程开始 sleep:10 秒
[20220518.173753.651][INFO ][Thread-1] 线程sleep前被设置中断,执行到sleep时,不睡,直接抛出中断异常,并清除中断标记: false

[20220518.173753.651][INFO ][Thread-1] 子线程开始 sleep:10 秒
[20220518.173754.652][INFO ][main] 主线程睡1秒后给子线程设置中断标记, 测试sleep中被设置中断
[20220518.173754.652][INFO ][Thread-1] 线程sleep中被设置中断,直接唤醒线程,抛出中断异常,并清除中断标记: false

[20220518.173754.652][INFO ][Thread-1] 子线程开始 wait:10 秒
[20220518.173754.652][INFO ][Thread-1] 线程wait前被设置中断,执行到wait时,不阻塞,直接抛出中断异常,并清除中断标记: false

[20220518.173754.652][INFO ][Thread-1] 子线程开始 wait:10 秒
[20220518.173755.656][INFO ][main] 主线程再睡1秒后给子线程设置中断标记, 测试wait中被设置中断
[20220518.173755.656][INFO ][Thread-1] 线程wait中被设置中断,直接唤醒线程,抛出中断异常,并清除中断标记: false

[20220518.173755.656][INFO ][Thread-1] 子线程开始 join:10 秒
[20220518.173755.656][INFO ][Thread-1] 线程join前被设置中断,执行到join时,不阻塞,直接抛出中断异常,并清除中断标记(本质上是wait): false

[20220518.173755.656][INFO ][Thread-1] 子线程开始 join:10 秒
[20220518.173756.668][INFO ][main] 主线程再睡1秒后给子线程设置中断标记, 测试join中被设置中断
[20220518.173756.668][INFO ][Thread-1] 线程join中被设置中断,直接唤醒线程,抛出中断异常,并清除中断标记(本质上是wait): false

[20220518.173756.668][INFO ][Thread-1] 子线程结束

[20220518.173757.682][INFO ][main] 主线程再睡1秒等待子线程测试完成
[20220518.173757.682][INFO ][main] 主线程结束

使用

@Test
@SneakyThrows
public void demo() {
    
    
	Thread thread = new Thread(() -> {
    
    
		while (!Thread.interrupted()) {
    
    
			try {
    
    
				log.info("开始做某事");
				Thread.sleep(600);
				log.info("结束做某事");
			} catch (InterruptedException e) {
    
    
				e.printStackTrace();
				log.info("中断异常, 清理资源");
				// 如果在sleep/wait/join时被设置中断, 中断标记会被清除
				// 针对该循环中的中断异常, 需要自己补充一下中断标记, 不然循环不会退出, 这里可以注释下来看运行效果
				Thread.currentThread().interrupt();
			}
		}
	});
	thread.start();

	TimeUnit.SECONDS.sleep(1);
	thread.interrupt();
	// 主线程终止的话, 子线程也会跟着终止, 所以让主线程多运行下, 方便看到 中断异常没有导致线程终止 的效果
	TimeUnit.SECONDS.sleep(1);
}
[20220518.181000.744][INFO ][Thread-1] 开始做某事
[20220518.181001.357][INFO ][Thread-1] 结束做某事
[20220518.181001.357][INFO ][Thread-1] 开始做某事
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at jdk.java.lang.ThreadInterruptTest.lambda$demo$2(ThreadInterruptTest.java:187)
	at java.lang.Thread.run(Thread.java:748)
[20220518.181001.749][INFO ][Thread-1] 中断异常, 清理资源

猜你喜欢

转载自blog.csdn.net/mrathena/article/details/124846673