探索Java并发编程利器:LockSupport,一种高效的线程阻塞与唤醒机制

news2024/11/26 0:34:46

关于作者:CSDN内容合伙人、技术专家, 从零开始做日活千万级APP。
专注于分享各领域原创系列文章 ,擅长java后端、移动开发、人工智能等,希望大家多多支持。

目录

  • 一、导读
  • 二、概览
  • 三、用法
  • 四、原理
  • 五、线程等待和唤醒的方法
    • 5.1 LockSupport.park()
      • Thread.sleep()和LockSupport.park()的区别
    • 5.2 Object中的wait()、notify、notifyAll
      • Object.wait()原理
    • 5.3 Condition的await()方法
    • 5.4 Thread.sleep()和Object.wait()的区别
    • 5.5 Object.wait()和LockSupport.park()的区别
    • 5.6 Object.wait()和Condition.await()的区别
    • 5.7 java中,notify/wait等方法为什么要在同步代码块中执行
    • 5.8 如果在wait()之前执行了notify()会怎样?
    • 5.8 如果在park()之前执行了unpark()会怎样?
  • 六、 推荐阅读

一、导读

我们继续总结学习Java基础知识,温故知新。

二、概览

LockSupport 是 Java SE 9 及以上版本中引入的一个线程同步工具类,用于支持同步方法,提供了多种同步机制.

LockSupport 所有的方法都是静态方法,可以让线程在任意位置阻塞,阻塞之后也有对应的唤醒方法。
LockSupport 底层调用的是Unsafe中的native代码。

java.util.concurrent并发包下很多并发类的底层加锁都是基于LockSupport,如ReentrantLock、CountDownLatch、ParkableLazyDeque 等。

三、用法

LockSupport 类中最常用的方法是 Park() 和 Unpark() 方法,分别用于阻塞和唤醒线程。


       Thread a = new Thread(() -> {

           System.out.println("start " + Thread.currentThread().getName());
           int resule = 0;
           for(int i = 0; i < 10; i ++) {
               resule += i;
           }
           // 阻塞线程
           LockSupport.park();
           System.out.println(resule);

       }, "a");

       a.start();
       try {
//            TimeUnit.MILLISECONDS.sleep(1000);
       } catch (Exception e) {
       }
       
       // 释放,这里不调用的话,上面将一直锁定
       LockSupport.unpark(a);

四、原理

park和unpark都是直接调用了Unsafe的方法,
我们这么理解:这两个方法的原理是基于一个计数器,计数器的值决定了线程的阻塞等待状态。当计数器的值减为 0 时,表示线程可以被唤醒,执行相应的操作。

此外,LockSupport 还提供了一些同步方法,如 Lock 和 Unlock() 方法,用于获取和释放锁。Lock 方法用于获取锁,Unlock() 方法用于释放锁。与 synchronized 关键字不同,Lock 和 Unlock() 方法不需要同时进行,可以在任意一个方法中获取锁,并且可以在任意一个方法中释放锁,因此更加灵活和方便。


    调用park后,此时线程处于waiting的状态
    
    public static void park() {
        UNSAFE.park(false, 0L);
    }
    
    public static void park(Object blocker) {
        // 获取当前线程
        Thread t = Thread.currentThread();
        
        // 设置Blocker
        setBlocker(t, blocker);
        
        // 此后,当前线程就已经阻塞了,后面的函数无法运行,直至unpark函数被调用
        UNSAFE.park(false, 0L);
        
        //unpark函数被调用后,继续执行,如果没有第二个setBlocker,那么之后没有调用park(Object blocker),
        //而直接调用getBlocker函数,得到的还是前一个park(Object blocker)设置的blocker,显然是不符合逻辑的
        setBlocker(t, null);
    }
    
    public static void unpark(Thread thread) {
        if (thread != null)
           UNSAFE.unpark(thread);
    }

Unsafe.java


    public native void unpark(Object var1);

    public native void park(boolean var1, long var2);

五、线程等待和唤醒的方法

5.1 LockSupport.park()

    // Hotspot implementation via intrinsics API
    private static final sun.misc.Unsafe UNSAFE;
    
    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            
        } catch (Exception ex) { throw new Error(ex); }
    }

Thread.sleep()和LockSupport.park()的区别

Thread.sleep()和LockSupport.park() 都是阻塞当前线程的执行,
都 不会 释放 当前线程占有的锁资源;

  • Thread.sleep()没法从外部唤醒,只能自己醒过来;本身就是一个native方法;
  • LockSupport.park()方法可以被另一个线程调用LockSupport.unpark()方法唤醒;* 底层是调用的Unsafe的native方法;

Thread.sleep()方法需要捕获中断异常。
LockSupport.park()不需要捕获中断异常。

5.2 Object中的wait()、notify、notifyAll

Object类中的wait、notify、notifyAll用于线程等待和唤醒的方法,都必须在sychronized内部执行

Object.wait()原理

在 Object.wait() 方法中,首先使用 synchronized 关键字锁定当前对象,然后调用 Object.wait() 方法等待另一个线程执行完毕并返回一个 Future 对象。在等待过程中,当前线程会一直阻塞,直到另一个线程执行完毕并返回一个 Future 对象。当等待线程执行完毕后,Future 对象的 get() 方法将返回等待线程的结果。因此,在使用 Object.wait() 方法时,需要确保等待线程已经执行完毕,否则可能会导致死锁等问题。

5.3 Condition的await()方法

Java中的Condition是java.util.concurrent.locks包下的一个接口,它主要用于在多线程环境中控制线程的等待和唤醒。

线程需要获得并持有锁,必须在锁块(synchronized或lock中)
必须要先等待后唤醒,线程才能够被唤醒。

5.4 Thread.sleep()和Object.wait()的区别

Thread.sleep()不会释放锁资源,到时间了会自动唤醒,然后继续执行;
Object.wait()会释放锁资源。需要另一个线程使用Object.notify()唤醒

5.5 Object.wait()和LockSupport.park()的区别

二者都会阻塞当前线程的运行。

  • Object.wait()方法需要在synchronized块中执行;

  • LockSupport.park()可以在任意地方执行;

  • Object.wait()方法需要捕获中断异常。

  • LockSupport.park()不需要捕获中断异常。

  • Object.wait()不带超时的,需要另一个线程执行notify()来唤醒,但不一定继续执行后续内容;

  • LockSupport.park()不带超时的,需要另一个线程执行unpark()来唤醒,一定会继续执行后续内容;

5.6 Object.wait()和Condition.await()的区别

Object.wait()和Condition.await()的原理是基本一致的,不同的是Condition.await()底层是调用LockSupport.park()来实现阻塞当前线程的。

5.7 java中,notify/wait等方法为什么要在同步代码块中执行

同步代码块通常由 synchronized 关键字修饰
在Java中,notify() 和 wait() 方法通常用于实现线程之间的通信和同步。这些方法通常在同步代码块中执行,以确保线程安全和原子性。
在同步代码块中执行 notify() 和 wait() 方法可以确保线程安全和原子性,因为它们可以保证在同一时刻只有一个线程可以执行这些方法。如果在非同步代码块中执行这些方法,则可能会导致竞争条件和死锁等问题。

5.8 如果在wait()之前执行了notify()会怎样?

如果当前的线程不是此对象锁的所有者,却调用该对象的notify()或wait()方法时抛出IllegalMonitorStateException异常;
如果当前线程是此对象锁的所有者,wait()将一直阻塞,因为后续将没有其它notify()唤醒它。

5.8 如果在park()之前执行了unpark()会怎样?

线程不会被阻塞,直接跳过park(),继续执行后续内容,大家可以自己尝试下


        Thread a = new Thread(() -> {

            System.out.println("start " + Thread.currentThread().getName());
            int resule = 0;
            for(int i = 0; i < 10; i ++) {
                resule += i;
            }
            LockSupport.park();
            System.out.println(resule);

        }, "a");

        try {
//            TimeUnit.MILLISECONDS.sleep(1000);
        } catch (Exception e) {
        }
        LockSupport.unpark(a);
        a.start();
        LockSupport.unpark(a);

六、 推荐阅读

Java 专栏

SQL 专栏

数据结构与算法

Android学习专栏

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

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

相关文章

Microsoft Edge 浏览器的Bing Chat

微软公司持续发力&#xff0c;推出的产品 Bing Chat 与 ChatGPT 之间的竞争愈发激烈。如今&#xff0c;微软不仅不断更新 Edge 浏览器&#xff0c;还将 Bing Chat 内置在边栏中&#xff0c;方便用户快速访问。这一举措不禁让人想起&#xff0c;Edge 浏览器如今已经是一款名副其…

Maven项目的两种打包方式-spring-boot-mavne-plugin/maven-jar-plugin

Maven项目的两种打包方式-spring-boot-mavne-plugin/maven-jar-plugin 1. 前言Maven的两种打包方式 2. 流程图3. spring-boor-maven-plugin打包4. maven-jar-plugin/maven-dependency-plugin打包 1. 前言 Maven的两种打包方式 spring-boot-maven-plugin springboot默认打包方…

新星计划联系人列表管理后台(一)-- 搭建项目开发环境

前言&#xff1a;对于前端的内容目前已经发现了很多大佬写的高质量文章&#xff0c;我这里就没必要再重复去造轮子了。接下来我会记录一下我从零到整完成此项目的过程&#xff0c;类似于日记&#xff0c;我会把我开发的步骤、过程中遇到的问题、如何解决此问题的思路、以及get到…

FPGA+EMMC 8通道存储小板

FPGA 采用XILINX公司A7100作为主芯片 AD采用AD7606及一款陀螺仪传感器&#xff0c;可以实时存储到EMMC&#xff0c;系统分为采集模式及回放模式 通过232接口对工作模式进行配置&#xff0c;采样率可以动态配置 回放采用W5100S通过TCP协议进行回放数据

福格行为模型

福格行为模型 福格行为模型也被称为“上瘾模型”&#xff0c;在工作和生活中应用非常的广泛。在工作中&#xff1a;产品设计、用户运营策略、活动设计、广告设计、游戏设计等等&#xff1b;在生活中&#xff1a;学习习惯培养、持续的健身、减肥计划等等。这些事通常大家以为是…

【N32L40X】学习笔记10-外部触发方式计数

定时器采用外部触发方式计数 也就是外部时钟源模式2 此模式由 TIMx_SMCTRL .EXCEN 选择等于 1。计数器可以在外部触发输入 ETR 的每个上升沿或下降沿 计数。 极性选择分频选择过滤选择选择外部时钟ETR模式 bsp_time_counter_ETR.h #ifndef _BSP_TIME_COUNTER_ETR_H_ #defi…

STM32(HAL库)驱动AD8232心率传感器

目录 1、简介 2、CubeMX初始化配置 2.1 基础配置 2.1.1 SYS配置 2.1.2 RCC配置 2.2 ADC外设配置 2.3 串口外设配置 2.4 GPIO配置 2.5 项目生成 3、KEIL端程序整合 3.1 串口重映射 3.2 ADC数据采集 3.3 主函数代码整合 4 硬件连接 5 效果展示 1、简介 本文通过STM32…

【车载性能优化】将线程进程运行在期望的CPU核心上

车载Android应用开发中&#xff0c;可能会出现一种奇葩的要求&#xff1a;与用户交互时应用需要全速运行&#xff0c;保证交互的流畅性&#xff0c;但是如果应用进入后台就需要怠速运行&#xff0c;让出更多的资源保证系统或前台应用的流畅度。那么基于这种需求&#xff0c;我们…

深度学习(31)——DeformableDETR(2)

深度学习&#xff08;31&#xff09;——DeformableDETR&#xff08;2&#xff09; 文章目录 深度学习&#xff08;31&#xff09;——DeformableDETR&#xff08;2&#xff09;1. backbone——Resnet502. neck——Channel mapper3. DeformableDETRHead4. DeformableDetrTransf…

Linux:多进程和多线程回环socket服务器和客户端

多进程socket服务器代码&#xff1a; #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <string.h> #include <ctype.h> #include <sys/wait.h> #i…

JWT 使用

前端访问后台a系统和b系统访问 Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准&#xff08;(RFC 7519).该token被设计为紧凑且安全的&#xff0c;特别适用于分布式站点的单点登录&#xff08;SSO&#xff09;场景。 JWT的本质就是一个…

记录安装DragGAN遇到的问题

首先第一个是安装的问题 安装的envirenment.yml有很多包过时了下载不了&#xff0c;可以参考以下文章写一个requirement.txt来下载依赖环境 --extra-index-url https://download.pytorch.org/whl/cu118 numpy1.23.5 click scipy pillow9.5.0 requests tqdm4.65.0 ninja matpl…

哪些语句会被waf屏蔽?

什么是waf&#xff1a;Web应用防火墙&#xff0c;Web Application Firewall的简称。 waf的功能&#xff1a;WAF可以发现和拦截各类Web层面的攻击&#xff0c;记录攻击日志&#xff0c;实时预警提醒&#xff0c;在Web应 用本身存在缺陷的情况下保障其安全。 封IP &#xff1a;…

Qt应用开发——QLineEdit

目录 一、概述 二、属性和方法 三、信号 一、概述 QLineEdit允许用户输入和编辑单行纯文本&#xff0c;并可以使用快捷编辑功能&#xff0c;包括复制、粘贴、剪切和拖放。是项目开发中最常用的输入控件。 默认键绑定描述如下。 Left Arrow //将光标向左移…

Fourier变换极其应用(Brad G. Osgood)——第1章——Fourier级数

目录 第1章 Fourier级数 1.1 选择&#xff1a;“欢迎入局”(Choices: Welcome Aboard) 1.2 周期性现象(Periodic phenomena) 1.2.1 时间和空间(time and space) 1.2.1.1 时间和空间周期性在波动中最自然地结合在一起 1.2.1.2 更多关于空间的周期性例子 1.2.2 定义&…

11 简单的Thymeleaf语法

11.1 spring-boot环境准备 重要依赖&#xff1a; <!--thymeleaf--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> 11.2 转发消息不转义 就是如…

picgo Request failed with status code 404

今天写picgo的时候&#xff0c;出现了一个错误&#xff0c;如何解决&#xff1a; 这里是repo的配置出现了问题&#xff0c;不过我的是因为粗心&#xff0c;把master写成了mater&#xff0c;emmmm 这里的repo要跟仓库的地址相同就是这一块&#xff1a;把这一块填到repo就行 然…

Linux查看二进制文件

Linux查看二进制文件 hexdump、hd、od、xxd hexdump、hd 可以使用16进制、10进制、8进制、ascii码的形式查看文件。 执行 ls -al which hd就会看到hd其实只是hexdump的一个软链接。 使用man hexdump&#xff0c;可以查看hexdump的各种参数。 -b, --one-byte-octal 单字节…

使用SQL JOIN语句把来自两个或多个表的行结合起来

先创建一个表&#xff1a; 创建一个名为 websites 的表 CREATE TABLE websites ( id int, name varchar(255), url varchar(255), alexa varchar(255), country varchar(255) ); 然后给它插入一些数据&#xff08;插入了四行新数据&#xff09;&#xff1a; insert into we…

详解go的hex.Encode原理

简言 今天看nsq的messageID生成的时候&#xff0c;发现它使用了hex.Encode函数来产生编码&#xff0c;那就顺道研究一下这个编码方式。 原理 hex是16进制的意思&#xff0c;encode是进行编码的意思&#xff0c;内部实现也很简单&#xff0c;就是 每4位计算出十六进制的值&a…