【JUC基础】09. LockSupport

news2025/1/19 7:52:38

1、什么是LockSupport

 

LockSupport是一个线程阻塞工具,可以在线程任意位置让线程阻塞。线程操作阻塞的方式其实还有Thread.suspend()和Object.wait()。而LockSupport与suspend()相比,弥补了由于resume()方法而导致线程被挂起(类似死锁)的问题,也弥补了wait()需要先获得某个对象锁的问题,也不会抛出InterruptedException异常。

相关API:

2、suspend()和resume()

说到线程阻塞,除了Object.wait()和Object.notify()两种以外,还有Thread.suspend()和Thread.resume()。不过当前这两组API已被弃用,因为他们可能会导致死锁情况发生。

示例代码:

package locksupport;

import java.util.concurrent.TimeUnit;

/**
 * @author Shamee loop
 * @date 2023/5/19
 */
public class ThreadSuspendTest {

    static Object syncObject = new Object();
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            synchronized (syncObject) {
                System.out.println(Thread.currentThread().getName() + "执行中,准备挂起");

                // 线程挂起
                Thread.currentThread().suspend();
            }
        }, "Thread-1");


        Thread thread2 = new Thread(() -> {
            synchronized (syncObject) {
                System.out.println(Thread.currentThread().getName() + "执行中,准备挂起");
                // 线程挂起
                Thread.currentThread().suspend();
            }
        }, "Thread-2");

        thread1.start();
        TimeUnit.SECONDS.sleep(1);
        thread2.start();
        // 继续执行
//        System.out.println("thread1线程准备执行了resume");
        thread1.resume();
//        System.out.println("thread1线程执行了resume");
        // 继续执行
//        System.out.println("thread2线程准备执行了resume");
        thread2.resume();
//        System.out.println("thread2线程执行了resume");
        thread1.join();
        thread2.join();
        System.out.println("程序运行结束");

    }
}

这里模拟了两个线程,运行过程中suspend()挂起,然后使用resume()让线程继续执行。

运行结果:

可以看到程序无法退出,也无法打印“程序运行结束”日志。这就说明程序被永远挂起。原因是suspend()在导致线程暂停的同时,不会释放任何资源。此时其他线程想要访问被占用的锁时,都会导致阻塞。直到线程上进行了resume(),被挂起的线程才能继续。但是如果resume()方法操作以外的在suspend()之前进行了,那么被挂起的资源就尽可能永远被挂起而无法继续。

这里更加注意的时候,这时候被挂起的线程,状态还是Runnable,这些估计也是JDK不推荐的原因吧。

我们将上面几行注释的代码打开,在运行以下:

我们发现thread2的resume()方法在suspend()之前就进行了。因此也解释了为什么thread2没有被继续执行的原因。

3、park()和unpark()

官方文档说了,LockSupport有两个重要的方法,park()和unpark()。而这两个方法很好的弥补了suspend()和resume()的不足。我们上述同等代码,用LockSupport来试下。

package locksupport;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

/**
 * @author Shamee loop
 * @date 2023/5/19
 */
public class LockSupportTest {

    static Object syncObject = new Object();
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            synchronized (syncObject) {
                System.out.println(Thread.currentThread().getName() + "执行中,准备挂起");
                LockSupport.park();
            }
        }, "Thread-1");


        Thread thread2 = new Thread(() -> {
            synchronized (syncObject) {
                System.out.println(Thread.currentThread().getName() + "执行中,准备挂起");
                LockSupport.park();
            }
        }, "Thread-2");

        thread1.start();
        TimeUnit.SECONDS.sleep(1);
        thread2.start();

        // 继续执行
//        System.out.println("thread1线程准备执行了unpark");
        LockSupport.unpark(thread1);
//        System.out.println("thread1线程执行了unpark");
        // 继续执行
//        System.out.println("thread2线程准备执行了unpark");
        LockSupport.unpark(thread2);
//        System.out.println("thread2线程执行了unpark");

        thread1.join();
        thread2.join();
        System.out.println("程序运行结束");
    }
}

运行结果:

可以看到程序正常退出,也打印出“程序运行结束”日志。而代码中只是将原本的suspend()方法替换成LockSupport.park(),将thread1.resume()替换成LockSupport.unpark(thread1)。

这是因为LockSupport使用的是类似信号量的机制。他为每个线程准备了一个许可,如果许可可用,那么park()方法会立刻返回,并且消费这个许可(也就是将许可变成不可用)。如果许可不可用,就会阻塞,而unpark()则是使得一个许可变成可用,但是和信号量不同的时,许可不能累加,不可能拥有超过一个许可。

所以这个特点就使得,即使unpark()方法执行发生在park()之前,他也可以是下一次的park()方法立即返回(因为当前的许可是可用的)。

不仅如此,通常使用park()挂起的状态,不会像suspend()还是Runnabele,他会直接给出waiting状态,同时堆栈也会体现由于park()方法引起,因此相比suspend()来说也更加友好。

我们将上述代码的注释打开,看下运行结果:

可以看到,thread2的unpark()就算运行在了park()之前,程序依然正常运行结束。

4、中断影响

LockSupport方法还支持中断影响。但是他的中断并不是直接抛出InterruptedException,而是默默返回,我们可以从Thread.interrupted方法中获得中断标记。

如代码:

package locksupport;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

/**
 * @author Shamee loop
 * @date 2023/5/19
 */
public class LockSupportTest {

    static Object syncObject = new Object();
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(LockSupportTest::doExecuteOn, "Thread-1");
        Thread thread2 = new Thread(LockSupportTest::doExecuteOn, "Thread-2");

        thread1.start();
        TimeUnit.SECONDS.sleep(1);
        thread2.start();

        thread1.interrupt();
        LockSupport.unpark(thread2);

    }

    private static void doExecuteOn(){
        synchronized (syncObject) {
            System.out.println(Thread.currentThread().getName() + "执行中,准备挂起");
            LockSupport.park();
            if(Thread.interrupted()){
                System.out.println(Thread.currentThread().getName() + "中断了");
            }
            System.out.println(Thread.currentThread().getName() + "执行结束");
        }
    }

}

执行结果:

我们可以看到thread1可以立马响应中断,并且返回,甚至都不用unpark()。返回后,外面的thread2才能占用资源,并最终由unpark(thread2)操作,程序结束。

5、小结

前面在《【JUC基础】08. 三大工具类》中我们介绍了三大使用的线程辅助类,这里单独介绍线程阻塞控制类。因为我觉得这个工具类和前面的辅助类还是不太一样的。线程的阻塞,唤起是多线程开发中不可或缺的,因此单独拿出来讲。这里使用简单的代码程序说明,希望能有所收获。一天进步一点点~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/556421.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

chatgpt赋能Python-python_bin目录

了解Python中的Bin目录 如果你是Python编程的初学者,可能会对Python中的Bin目录感到困惑。本文将为你介绍Bin目录的作用,以及为什么它对于Python编程人员来说是如此重要。 什么是Bin目录? Bin目录是Python安装时创建的一个目录&#xff0c…

Linux Yum指令

目录 一.yum 二.使用yum下载软件指令 1.安装指令: 2.查找在yum中所下载软件的指令: 3.卸载指令: 一.yum yum是Linux的一个指令,其实它的作用相当于一个商店,我们可以在商店里面买我们需要的东西。 我们将yum称为包…

40亿个QQ号,限制1G内存,如何去重?

40亿个QQ号,限制1G内存,如何去重? 40亿个unsigned int,如果直接用内存存储的话,需要: 4*4000000000 /1024/1024/1024 14.9G ,考虑到其中有一些重复的话,那1G的空间也基本上是不够…

chatgpt赋能Python-python_calchist

Python计算直方图:使用calchist进行图像分析 如果你是一位熟练掌握Python的工程师,同时又对图像分析领域感兴趣,那么你应该一定听说过Python的OpenCV库。OpenCV是一个专业的图像处理库,可以用来处理图像、视频和计算机视觉等相关…

2023 华为 Datacom-HCIE 题库 03--含解析

单选题 1.[试题编号:189810] (单选题)SSH算法协商阶段中,双方交换自身所支持的算法列表。SSH服务器的对称加密算法列表依次为:aes256-ctr,aes192-ctr,aes128-ctr,aes256-cbc。SSH客户端的对称加密算法列表依次为:aes1…

研发工程师玩转Kubernetes——使用Deployment进行版本升级

软件升级是一件非常常见的事,本节我们将尝试使用Deployment进行软件升级。 更新simple_http版本 我们还是借助《研发工程师玩转Kubernetes——构建、推送自定义镜像》中的代码库,只是稍微修改一下Dockerfile——将版本变成2。 From python:3.11 RUN p…

TypeScript - Interfaces(接口)

目录 1、接口介绍 1.1 接口示例 2、可选属性 3、只读属性 4、额外的属性检查 5、函数类型 6、可索引的类型 7、类类型 7.1 类静态部分和实例部分 8、继承接口 9、混合类型 10、接口继承类 1、接口介绍 Ty…

探索未来:物联网的无限可能

连接万物,创造未来。从智能家居到智慧医疗,从智能车联到智慧城市,物联网技术的影响已经悄然渗透到了我们的方方面面。欢迎大家积极讨论联网技术如何影响了我们的生活。 物联网技术概述 物联网技术(Internet of Things&#xff0…

WebStorm 固定 调试版 Chrome

WebStorm 固定 调试版 Chrome 每次升级 WebStorm 都会打开一个新的 Chrome,导致调试时需要重新登录,重新安装插件等问题。 解决办法: 固定 WebStorm 中 Chrome UserData 的路径,这样每次打开的 Chrome 都是同一个。 文件 | 设…

HJ73 计算日期到天数转换

1.题目&#xff1a; 2.分析&#xff1a; 1. 通过枚举每个月的1号是这一年的第几天&#xff0c;从而进行累加求和即可&#xff0c;其中注意闰年的处理 3.我的代码&#xff1a; #include <iostream> using namespace std;int main() {int arr[13] { 0, 31, 28, 31, 30,…

设计模式基础-面向对象基础

✨作者&#xff1a;猫十二懿 ❤️‍&#x1f525;账号&#xff1a;CSDN 、掘金 、个人博客 、Github &#x1f389;公众号&#xff1a;猫十二懿 这里只是简单的将《大话设计模式【Java溢彩加强版】》的内容简单是复述一下&#xff0c;并加上自己的理解 在学习Java设计模式的时候…

Hadoop集群实现时间同步

一.为什么要对集群实现时间同步 因为我们在集群使用hive&#xff0c;mysql&#xff0c;hdfs之间等使用sqoop传输数据的时候&#xff0c;如果集群之间没有同步时间的话&#xff0c;那么就会报错&#xff0c;无法实现数据的传输。 不仅如此&#xff0c;在集群的使用当中&#xff…

logstash的快速使用

同品&#xff1a; filebeat&#xff1a;只做数据收集&#xff0c;讲数据输出到指定目的地&#xff0c;占用资源小 logstash:收集日志数据&#xff0c;还能过滤&#xff0c;转换数据&#xff0c;组需要更多资源 目录 1.logstash的安装 2.配置文件 3.创建容器 4.引入依赖 …

设计模式期末程序题(只是一个简单整理)

1.下图是某系统的数据部分的类图。因为该层次结构中的操作需要经常变化&#xff0c;所以需要用访问者模式对其进行重构&#xff0c;请按以下要求完成题目&#xff1a; &#xff08;1&#xff09;绘制重构后系统完整类图。&#xff08;4分&#xff09; &#xff08;2&#xff09…

第一章 数学基础

目录 一、线性代数二、微积分三、概率 一、线性代数 理解范数概念区分向量的内积 a ⋅ b \mathbf{a} \cdot \mathbf{b} a⋅b 与外积 a b \mathbf{a} \times \mathbf{b} ab区分矩阵的乘法 A ⊗ B \mathbf{A} \otimes \mathbf{B} A⊗B、内积 A B \mathbf{A} \mathbf{B} AB 、…

通过Python的PyPDF2库提取pdf中的图片

文章目录 前言一、PyPDF2库是什么&#xff1f;二、安装PyPDF2库三、查看PyPDF2库版本四、使用方法待提取的pdf截图1.引入库2.定义pdf路径3.打开PDF文件4.创建PDF阅读器对象5.获取PDF文件中的页数6.遍历每一页进行处理7.提取出来的图片 总结 前言 大家好&#xff0c;我是空空sta…

【Linux初阶】进程程序替换 | 初识、原理、函数、应用 makefile工具的多文件编译

&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f; &#x1f36d;&#x1f36d;系列专栏&#xff1a;【Linux初阶】 ✒️✒️本篇内容&#xff1a;替换初识&#xff0c;替换原理&#xff0c;替换函数理解和使用&#xff0c;makefile工具的多文件编译&#xf…

C++(4):表达式

表达式由一个或多个运算对象(operand)组成,对表达式求值将得到一个结果(result&#xff09;。字面值和变量是最简单的表达式&#xff08;expression)&#xff0c;其结果就是字面值和变量的值。把一个运算符&#xff08;operator)和一个或多个运算对象组合起来可以生成较复杂的表…

倒挂的解决方案你现在是一位计算机专家,来聊一聊:“美国的火星探测器Mars Path-finder 就是因为优先级倒挂而出现故障的故事”

目录 倒挂的解决方案 你现在是一位计算机专家&#xff0c;来聊一聊&#xff1a;“美国的火星探测器Mars Path-finder 就是因为优先级倒挂而出现故障的故事” ●使用中断禁止 具体证明请参阅Liu和Kayland于1973年发表的论文。 ● 因时序或外部中断或进程挂起而导致操作系统获…

数据结构-关键路径-理论

1.AOE-网 与AOV-网相对应的是AOE-网&#xff08;Activity On Netword&#xff09;&#xff0c;即以边表示活动的网。AOE-网是带权的有向无环图&#xff0c;其中&#xff0c;定点表示时间&#xff0c;弧表示活动&#xff0c;权表示活动持续的时间。通常AOE-网可用来估算工程的完…