【JUC(三)】中断与等待唤醒

news/2024/5/20 5:47:39 标签: JUC, 并发编程

1. interrupt() 相关方法

interrupt(),interrupted()isinterrupted() 的区别

public void interrupt():

将线程的中断标记设置为 true,并不是真的停止该线程。
如果线程处于  wait()、join()、sleep() 方法的阻塞当中。中断标记会被清除。也就是通过isInterrupted,interrupted 方法返回均为 false。同时抛出一个InterruptedException异常。

public boolean isInterrupted()
判断某个具体线程对象是否被中断,返回的是线程的中断标记(true or false)

public static boolean interrupted()
返回当前线程的中断状态,然后将当前线程的中断状态设为false。
如果线程中断标记为true,只有第一次调用时会返回 true。之后调用都是false。(这个方法会清除线程的中断状态)
  • 三者的使用
public static void interruptMethod03() throws InterruptedException {
    Thread t1 = new Thread(()->{
        // 当要 interrupt 一个正常运行线程时,
        // 线程的打断标记会被赋值为 true ,但是线程不会立刻中断。
        Thread now = Thread.currentThread();
        int i = 0;
        boolean flag = false;
        while(true){
            flag = Thread.interrupted();
            System.out.println(++i);
            if(flag){
                // 可以说明:Thread.interrupted() 是清除打断标记
                System.out.println("t1.interrupt()执行后,第一次调用 Thread.interrupted(). 应该返回true");
                System.out.println("flag: "+ flag);
                System.out.println("t1.interrupt()执行后,第二次调用 Thread.interrupted(). 应该返回false");
                System.out.println("Thread.interrupted(): "+Thread.interrupted());

                System.out.println();

                System.out.println("再一次调用 t1.interrupt() 之后");
                now.interrupt();
                System.out.println("now.isInterrupted()【不会将打断状态设置为false】. 应该返回true");
                System.out.println("now.isInterrupted(): "+now.isInterrupted());

                System.out.println("Thread.interrupted()【将打断状态设置为false】.当前应该返回true");
                System.out.println("Thread.interrupted(): "+Thread.interrupted());
                System.out.println("经过Thread.interrupted()对打断状态的清除,now.isInterrupted()应该返回false,now.isInterrupted(): "
                                   +now.isInterrupted());
                // 当前线程已经被打断(也即其他线程调用了当前线程的interrupt方法)
                System.out.println(now.getName() + " 线程被打断.....");
                break;
            }
        }
    },"t1");
    t1.start();
    Thread.sleep(100);
    t1.interrupt();
}

通过上面的例子可以了解到:

在Java中没有办法立即停止一条线程(处于阻塞状态除外),然而停止线程却显得尤为重要,如取消一个耗时操作。

因此,Java提供了一种用于停止线程的协商机制——中断

中断只是一种协作协商机制,Java没有给中断增加任何语法,中断的过程完全需要程序员自己实现。

比如线程A调用了线程Bpublic void interrupt():方法。线程B只能在 run 方法中使用 isInterruptedinterrupted 方法的返回值确定是否中断,然后自行编写中断逻辑。

  • 阻塞线程被中断的情况
public static void main(String[] args) {
    Thread t1 =  new Thread(()->{
        while(true){
            if(Thread.currentThread().isInterrupted()){
                System.out.println(Thread.currentThread().getName()+"\t"+
                                   "中断标志位:"+Thread.currentThread().isInterrupted()+"程序终止");
                break;
            }
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
                // 加了这个,程序可以终止,只会爆异常
                // 如若不在异常处理部分加入 interrupt,程序会一直报异常。
                // 因为 处于阻塞的线程被 interrupt 后,会将线程中断标记设置为false。
                // 这样就导致 Thread.currentThread().isInterrupted() 返回false,程序无法中断
                Thread.currentThread().interrupt();
            }
            System.out.println("-----hello InterruptDemo03");
        }
    },"t1");
    t1.start();
    try {
        TimeUnit.MILLISECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}
    new Thread(() -> t1.interrupt()).start();
}

2. LockSupport

LockSupport是用来创建锁其他同步类基本线程阻塞原语,其中park()unpack()而作用分别是阻塞线程解除阻塞线程

3. 线程的等待唤醒机制

  • 方式一:使用Object中的wait()方法让线程等待,使用Object中的notify()方法唤醒线程
  • 方式二:使用JUC包中的Conditionawait()方法让线程等待,使用signal()方法唤醒线程
  • 方式三:LockSupport类可以阻塞当前线程以及唤醒指定被阻塞的线程

3.1 Object中wait和notify

  • waitnotify需要成对出现,且需要在同步代码块或同步方法中使用。
  • 要先 wait,后 notify
public static void main(String[] args) {
    Object objectLock = new Object();

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

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

    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    new Thread(()->{
        synchronized(objectLock){
            //  如果只有两个线程,一个等待,一个唤醒。就调用notify
            // objectLock.notify();
            // 如果有多个线程等待,就需要调用 notifyAll。 一次唤醒全部等待线程
            objectLock.notifyAll();
            System.out.println(Thread.currentThread().getName()+" 唤醒通知已经发送。。。");
        }
    },"Thread 02").start();
}

3.2 Condition中await和signal

  • Condition中的线程等待和唤醒方法,需要先获取锁
  • 一定要先await后signal,不要反了
  • Condition可以拥有多个,这样面对不同的场景,可以创建不同的 condition。解决 Object对象中多个wait只能notifyAll的情况。
// 方式二:使用`JUC`包中的`Condition`的`await()`方法让线程等待,使用`signal()`方法唤醒线程
public static void awaitAndSignal(){
    Lock lock = new ReentrantLock();
    // 可以针对不同的业务拥有不同的 condition,等待唤醒可以达到具体某个线程的粒度。
    Condition condition = lock.newCondition();
    Condition conditionThread03 = 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();
        }
    },"Thread 01").start();

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

    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

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

3.3 Object和Condition使用的限制条件

  • wait 与 notify 必须在 synchronized 快中使用,否则会报异常。
  • await 与 signal 需要配合 lock 使用。
  • 二者的等待与唤醒的顺序是不能错的。

3.4 LockSupport类中的park等待和unpark唤醒

  • LockSupport 使用了一种名为Permit(许可)的概念来做到阻塞和唤醒线程的功能
  • 每个线程都有一个许可(permit),且累加上限为1(也就是说,要么有一个许可,要么没有许可)。
  • 当程序调用 LockSupport.park();时,如果当前线程没有许可,就进行阻塞,有许可就放行。
  • LockSupport.unpark(t1);t1 线程一个许可(permit),

例子:

// 方式三:`LockSupport`类可以`阻塞当前线程`以及`唤醒指定被阻塞的线程`
// LockSupport 使用了一种名为Permit(许可)的概念来做到阻塞和唤醒线程的功能.每个线程都有一个许可(Permit),许可证只能有一个,累加上限是1。
public static void parkAndUnpark(){
    /**
      * t1	 -----------come in
       * t2	 ----------发出通知
       * t1	 ----------被唤醒
       */
    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();
}

例子:先调用 unpark , 在调用 park。不会报异常,也不会阻塞。

/*
    先调用 unpark 后调用 park
     */
public static void parkAndUnpark02(){
    	/**
         * t1	 -----------come in
         * t2	 ----------发出通知
         * t1	 ----------被唤醒
         */
    Thread t1 = new Thread(() -> {
        System.out.println(Thread.currentThread().getName() + "\t -----------come in");
        System.out.println(Thread.currentThread().getName() + "\t ----------unpark");
        LockSupport.unpark(Thread.currentThread());

        System.out.println(Thread.currentThread().getName() + "\t ----------park");
        // 上面已经 unpark ,得到了一个 permit。 
        // 所以调用一次 park 不会产生阻塞。
        // 但是如果调用2两次及以上,就会产生阻塞。应该 permit 不累加,最大为1。
        LockSupport.park();
        System.out.println(Thread.currentThread().getName() + "不会阻塞");
    }, "t1");
    t1.start();
}
  • 其他方法
权限方法签名与解释
static ObjectgetBlocker(Thread t)Returns the blocker object supplied to the most recent invocation of a park method that has not yet unblocked, or null if not blocked.
static voidpark()Disables the current thread for thread scheduling purposes unless the permit is available.
static voidpark(Object blocker)Disables the current thread for thread scheduling purposes unless the permit is available.
static voidparkNanos(long nanos)Disables the current thread for thread scheduling purposes, for up to the specified waiting time, unless the permit is available.
static voidparkNanos(Object blocker, long nanos)Disables the current thread for thread scheduling purposes, for up to the specified waiting time, unless the permit is available.
static voidparkUntil(long deadline)Disables the current thread for thread scheduling purposes, until the specified deadline, unless the permit is available.
static voidparkUntil(Object blocker, long deadline)Disables the current thread for thread scheduling purposes, until the specified deadline, unless the permit is available.
static voidunpark(Thread thread)Makes available the permit for the given thread, if it was not already available.

http://www.niftyadmin.cn/n/1008583.html

相关文章

uview-plus上传图片,upload组件带参数上传

一、引入uview-plus 请自行在项目中引入uview-plus组件库&#xff0c;此处不多赘述 二、使用 html 部分&#xff0c;上传组件的样式自己去定义&#xff0c;不多赘述 <u-upload:fileList"fileList" // 文列列表afterRead"afterRead" // 读取后的…

【C++面向对象】足球比赛数据统计系统(面向对象练习)

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、51CTO技术博主 &#x…

阿里云ECS U实例评测

参与ECSU实例评测&#xff0c;申请免费体验机会&#xff1a;https://developer.aliyun.com/mission/review/ecsu What u1实例是什么&#xff1f; u1实例本质上还是ecs服务器&#xff0c;但是是阿里云推出的一种新型实例规格族 阿里云根据使用场景和业务场景将ecs划分为不同的…

Airtest:Windows桌面应用自动化测试二【Airtest基于图像识别自动控制手机App流程】

Airtest基于图像识别自动控制手机App流程 一、Airtest基于图像识别自动控制手机App流程二、基于图像识别生成脚本有两种操作&#xff1a;三、Airtest基于Poco的UI组件自动化控制App流程四、Airtest实现手机群控操作 Airtest介绍与脚本入门 Airtest相关api操作 一、Airtest基于…

BUG等级定义及划分--测试与产品针对不同等级采取不同策略

系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录 系列文章目录前言一、BUG等级定义一级(致命)二级(严重)三级(一般)四级(轻微)二、偶现BUG定义时效性一级(致命)二级(严重)三级(一般)前言 认知有限,望…

提升文件管理效率:轻松批量归类文件,按名称细分管理

现代生活中&#xff0c;我们每天都面对着大量的电子文件&#xff0c;如文档、照片、音乐和视频等。这么多文件堆积在一起&#xff0c;怎样快速找到需要的文件成了一个挑战。现在有应该方法可以帮助您提升文件管理效率&#xff0c;方法如下&#xff1a; 首先&#xff0c;第一步…

多元回归预测 | Matlab基于粒子群算法(PSO)优化混合核极限学习机HKELM回归预测, PSO-HKELM数据回归预测,多变量输入模型

文章目录 效果一览文章概述部分源码参考资料效果一览 文章概述 多元回归预测 | Matlab基于粒子群算法(PSO)优化混合核极限学习机HKELM回归预测, PSO-HKELM数据回归预测,多变量输入模型 评价指标包括:MAE、RMSE和R2等,代码质量极高,方便学习和替换数据。要求2018版本及以上。…

[LeetCode周赛复盘] 第 326 场周赛20230702

[LeetCode周赛复盘] 第 326 场周赛20230702 一、本周周赛总结6909. 最长奇偶子数组1. 题目描述2. 思路分析3. 代码实现 6916. 和等于目标值的质数对1. 题目描述2. 思路分析3. 代码实现 6911. 不间断子数组1. 题目描述2. 思路分析3. 代码实现 6894. 所有子数组中不平衡数字之和…