【JUC2022】第三章 线程中断与 LockSupport

文章介绍了Java并发编程中的线程中断机制,强调中断是一种协商而非强制停止线程的方式,通过`interrupt()`方法设置中断标识。文章详细讲解了`Thread.sleep()`如何响应中断,并提供了代码示例展示如何中断和检查线程状态。此外,还讨论了LockSupport工具类的`park()`和`unpark()`方法,作为线程阻塞和唤醒的原语,并对比了与wait/notify以及Condition的异同。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【JUC2022】第三章 线程中断与 LockSupport

一、线程中断

1.什么是中断机制

首先,一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己停止,自己来决定自己的命运。所以,Thread.stop,Thread.suspend,Thread.resume 都已经被废弃了

其次,在 Java 中没有办法立即停止一条线程,然而停止线程却尤为重要,如取消一个耗时操作。因此,Java 提供了一种用于停止线程的协商机制——中断,也即中断标识协商机制

中断只是一种协商机制,Java 没有给中断增加任何语法,中断的过程完全需要程序员自己实现
若要中断一个线程,需要手动调用该线程的 interrupt 方法,该方法也仅仅是将线程对象的中断标识设成 true。接着你需要自己写代码不断地检测当前线程的标识位,如果为 true,表示别的线程请求这条线程中断,此时究竟该做什么需要自己写代码实现

每个线程对象中都有一个中断标识位,用于表示线程是否被中断。该标识位为 true 表示中断,为 false 表示未中断。通过调用线程对象的 interrupt 方法将该线程的标识位设置为 true,可以在别的线程中调用,也可以在自己的线程中调用

2.中断 API

public void interrupt()
设置线程的状态为 true,发起一个协商而不会立刻停止线程
如果该线程已经执行了 wait()、join()、sleep()方法,此时其他线程调用该线程的 interrupt() 方法,那么该线程将立即退出阻塞状态,并抛出 InterruptedException

public static boolean interrupted()
返回当前线程的中断状态,并将当前线程的中断标识位重新设置为 false

这里可能会有一点抽象,读者朋友可能会疑惑为什么要重新设置中断标识位为 false。我觉得这只是一个表述问题,因为这个方法的主要功能就是将线程的中断标识位设置为 false,而不是返回中断状态。我们会发现,如果没有这个方法,我们就没有其他方法去重置线程的中断标识位了

public boolean isInterrupted()
返回当前线程的中断标识位

3.代码实现

package com.sisyphus.Interrupt;

import java.util.concurrent.TimeUnit;

public class InterruptDemo {
    public static void main(String[] args) {
        Thread t1 = new Thread(()->{
            while(true){
                if (Thread.currentThread().isInterrupted()){
                    System.out.println(Thread.currentThread().getName() + "线程被中断");
                    break;
                }
                System.out.println("线程运行中...");
            }
        },"t1");
        t1.start();

        //暂停
        try{
            TimeUnit.MILLISECONDS.sleep(20);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
        
        new Thread(()->{
            t1.interrupt();
        },"t2").start();
    }
}

4.Thread.sleep()

为什么 Thread.sleep() 要抛中断异常
为了让线程具备感知中断能力。不能让线程无感知地一直睡眠,总会有需要“叫醒”它的突发情况

为什么 interrupt() “叫醒了”线程后要清除中断标志位
为了让线程能够继续执行本该执行的代码。线程中往往会使用 if(!Thread.currentThread().isInterrupted()) 来达到判断线程是否在睡眠中被中断的目的

如果需要实现睡眠中被中断,就不继续执行 if 代码块怎么办
需要我们在 catch 代码块中手动调用 interrupt()

try {
    Thread.sleep(100);
} catch (InterruptedException e) {
    //中断标志已经被清除了
    // 手动中断本线程,将本线程打上中断信号。
    Thread.currentThread().interrupt();
}
// Thread.currentThread().isInterrupted():是否被中断了(是否有中断标志)
if(!Thread.currentThread().isInterrupted()) {
    //如果没有被中断,则处理业务
    doSomething();
}

二、LockSupport

1.什么是 LockSupport

java.util.concurrent.locks.LockSupport
定义:用于创建锁和其它同步类的基本线程阻塞原语

LockSupport 有 park() 和 unpark() 两个重要方法,其作用分别是阻塞线程和解除阻塞线程

3 种让线程等待和唤醒的方法

  1. 使用 Object 中的 wait() 和 notify()
  2. 使用 JUC 包中的 Condition 和 await() 和 signal()
  3. 使用 LockSupport 中的 park() 和 unpark()

方法1和方法2存在的限制:

  • 线程先要获得并持有锁
  • 必须先执行等待,才能执行唤醒

Permit
LockSupport 使用了一种名为 Permit(许可) 的概念来做到阻塞和唤醒线程,每个线程都有一个 Permit,与 Semaphore 不同的是,Permit 的累加上限是 1

2.代码实现

package com.sisyphus.LockSupport;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

public class LockSupportDemo {
    public static void main(String[] args) {
        Thread t1 = new Thread(()->{
            System.out.println(Thread.currentThread().getName() + "\t -----come in");
            LockSupport.park();
            System.out.println(Thread.currentThread().getName() + "\t -----被唤醒");
        },"t1");
        t1.start();

        //暂停几秒钟线程
        try{
            TimeUnit.SECONDS.sleep(1);
        }catch (InterruptedException e){
            e.printStackTrace();
        }

        new Thread(()->{
            LockSupport.unpark(t1);
            System.out.println(Thread.currentThread().getName() + "\t -----发出通知");
        },"t2").start();
    }

    public static void lockUnlock() {
        Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();

        new Thread(()->{
            lock.lock();
            try{
                System.out.println(Thread.currentThread().getName() + "\t -----come in");
                condition.await();
                System.out.println(Thread.currentThread().getName() + "\t -----被唤醒");
            }catch (InterruptedException e){
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        },"t1").start();

        //暂停几秒钟线程
        try{
            TimeUnit.SECONDS.sleep(1);
        }catch (InterruptedException e){
            e.printStackTrace();
        }

        new Thread(()->{
            lock.lock();
            try{
                condition.signal();
                System.out.println(Thread.currentThread().getName() + "\t -----发出通知");
            }finally {
                lock.unlock();
            }
        },"t2").start();
    }

    public static void waitNotify() {
        Object objectLock = new Object();

        new Thread(()->{
            synchronized (objectLock){
                System.out.println(Thread.currentThread().getName() + "\t -----come in");
                try{
                    objectLock.wait();
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "\t -----被唤醒");
            }
        },"t1").start();

        //暂停几秒钟线程
        try{
            TimeUnit.SECONDS.sleep(1);
        }catch (InterruptedException e){
            e.printStackTrace();
        }

        new Thread(()->{
            synchronized (objectLock){
                objectLock.notify();
                System.out.println(Thread.currentThread().getName() + "\t -----发出通知");
            }
        },"t2").start();
    }
}

3.总结

LockSupport 是一个线程阻塞工具类,所有的方法都是静态方法,可以让线程在任意位置阻塞,阻塞之后也有对应的唤醒方法。归根结底,LockSupport 调用的是 Unsafe 中的 native 方法

当调用 park() 时

  • 如果有凭证,则会直接消耗掉这个凭证,然后正常退出
  • 如果没有凭证,就必须阻塞等待凭证可用

当调用 unpark() 时,会增加一个凭证,但最多只有一个,累加无效
因此,当连续调用 park() 时,线程必然阻塞,无论之前调用过多少次 unpark()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

313YPHU3

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值