【JavaEE】volatile + final + wait-notify + join + park-unpark 相关原理

本文基于jdk8

本文所讲的一些原理都是在多线程中经常使用的内容。

参考:黑马程序员深入学习Java并发编程,JUC并发编程全套教程_哔哩哔哩_bilibili


目录

volatile原理

Java内存模型(JMM)

可见性&有序性

双重检查锁应用

final原理

设置final变量

读取final变量

wait-notify原理

wait与sleep的区别

join原理

park-unpark原理

park与wait的区别 


volatile原理

volatile是在多线程情况下一个常见的关键字,其作用的防止指令重排序和保证变量的可见性。要想了解其底层原理,要先知道Java内存模型相关的一些概念。


Java内存模型(JMM)

这里只是大概讲一下Java内存模型。

JMM决定一个线程共享变量的写入何时对另一个线程可见,JMM定义了线程和主内存之间的抽象关系:共享变量存储在主内存(Main Memory)中,每个线程都有一个私有的本地内存(Local Memory),本地内存保存了被该线程使用到的主内存的副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量。

可以大概这么理解


可见性&有序性

使用了volatile关键字后,某个线程对于共享变量的修改,其他线程可以立马知道最新修改后的值。

某个线程执行一些被volatile修饰的变量的代码是按照顺序来执行的。

这是用到了内存屏障。内存屏障由读屏障写屏障构成。

  • 可见性
    • 写屏障保证在该屏障之前对共享变量的改动都会同步到主存中
    • 读屏障保证在该屏障之后对共享变量的读取都是加载主存中最新的数据
  • 有序性
    • 写屏障保证指令重排序时,不会将写屏障之前的代码排在写屏障之后
    • 读屏障保证指令重排序时,不会将读屏障之后的代码排在读屏障之前
  • 总结就是写指令之后加入写屏障,读指令之前加入读屏障

双重检查锁应用

public final class Singleton {private Singleton() {}private static volatile Singleton INSTANCE = null;public static Singleton getInstance() {// 实例没创建,才会进入内部的 synchronized代码块if (INSTANCE == null) {synchronized (Singleton.class) {// 也许有其它线程已经创建实例,所以再判断一次if (INSTANCE == null) { // t1// t1是最开始调用这个方法的,并且是第一个执行到这里的线程// 下面这个赋值的语句可能会变成 先 INSTANCE=null,然后再执行构造方法 这样会造成 INSTANCE还是null的// 加了volatile后就能保证 构造方法已经执行完毕了INSTANCE = new Singleton();}}}return INSTANCE;}
}

可见性:这里的 INSTANCE 实例可能某个线程已经创建了,但是其他线程还不知道创建完成了。所以加了volatile后,后续其他线程访问导的都是最新数据。

指令重排序:就是上面的代码注释。


final原理

设置final变量

public class TestFinal {int a = 20;
}

上面的变量a如果没有用final修饰,其步骤分成初始化a变量,此时a为0,然后在赋值20.

在多线程的情况下,有可能刚给a初始化完成,就会有其他线程来读取a的值,它就会把 0 读走。

有final修饰的话,它在写的时候会加入写屏障,从而让其他线程读不到0

读取final变量

没有final修饰的变量,只能从共享内存读,从堆中读数据效率低
有final修饰的变量
  • 较小的值直接复制(没有超过每种类型所表示的最大值)
  • 如果超过最大值,则放到该类的常量池,然后读取

可以知道,不用final修饰读共享变量时效率并不高。


wait-notify原理

当一个对象调用wait方法时,那么这个线程就进入了阻塞,具体原因还是和对象头Mark Word某部分将会指向Monitor,从而变成重量级锁。

当然上面的wait方法如果有等待时间的话,超过时间后也会进入到EntryList中。

wait与sleep的区别

wait和sleep最本质的区别就是 线程中的对象调用wait方法后,该线程会进入WaitSet中,此时其他线程可以对 该对象进行加锁;线程调用sleep方法后,它只是等待设定的时间,此时线程不会释放自己所持有的锁。


join原理

join是Thread类中的一个方法,如果线程A中调用了 线程B的join方法,那么线程A只能等到 线程B执行完成后再往后继续执行。其实join的底层原理就是调用了 wait()方法,源码如下

public final synchronized void join(long millis)throws InterruptedException {long base = System.currentTimeMillis();long now = 0;if (millis < 0) {throw new IllegalArgumentException("timeout value is negative");}if (millis == 0) {while (isAlive()) {wait(0);}} else {while (isAlive()) {long delay = millis - now;if (delay <= 0) {break;}wait(delay);now = System.currentTimeMillis() - base;}}}

这里体现出保护性暂停模式,后续会介绍。 


park-unpark原理

park和unpark是LockSupport中的方法。LockSupport.part() 调用后就是暂停当前线程(不会释放锁资源),LockSupport.unpark(暂停线程的对象) 调用后让暂停的线程继续执行。

其底层由 “二元信号量来控制”,可以理解成 unpaik 时这个信号量就会加一,但不会超过一,park时就会减一,但不会超过零。

park与wait的区别

  • wait后会释放锁资源,但是park不会
  • wait唤醒的时候是随机的,park是精确唤醒
  • 如果在没有wait对象直接notify,就会抛出异常;如果没有park,先unpark,那么后续在执行到park时,就不会休眠。

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

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

相关文章

GPT-4o大语言模型优化、本地私有化部署、从0-1搭建、智能体构建

原文链接&#xff1a;GPT-4o大语言模型优化、本地私有化部署、从0-1搭建、智能体构建https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247608565&idx3&snd4e9d447efd82e8dd8192f7573886dab&chksmfa826912cdf5e00414e01626b52bab83a96199a6bf69cbbef7f7fe…

学习日志:java线程

文章目录 前言一、线程和进程线程与进程的关系 二、Java 线程和操作系统的线程JDK 1.2 之前JDK 1.2 及以后用户线程和内核线程 三、创建线程四、线程的生命周期和状态五、线程的上下文切换六、Thread#sleep() 方法和Object#wait() 方法 前言 这里总结线程的一些概念。 一、线程…

​探讨元宇宙和VR虚拟现实之间的区别​

在数字时代&#xff0c;人们对虚拟现实的兴趣与日俱增。在虚拟现实技术的推动下&#xff0c;出现了两个概念&#xff1a;元宇宙和VR虚拟现实。虽然这两个概念都与虚拟现实有关&#xff0c;但它们有着不同的特点和用途。在本文中&#xff0c;我们将探讨元宇宙和VR虚拟现实之间的…

香橙派AIpro-携手华为-为AI赋能

文章目录 香橙派AIpro-携手华为-为AI赋能开箱和功能介绍开箱功能介绍 环境搭建镜像烧录进入系统 测试项目YOLOv5部署YOLOv5识别单张图片实时识别视频使用Ascend测试yolov5 产品评价 香橙派AIpro-携手华为-为AI赋能 今天新入手了一款香橙派AIPro&#xff0c;让我们一起跟着文章…

接口防刷!利用redisson快速实现自定义限流注解

问题&#xff1a; 在日常开发中&#xff0c;一些重要的对外接口&#xff0c;需要加上访问频率限制&#xff0c;以免造成资&#xfffd;&#xfffd;损失。 如登录接口&#xff0c;当用户使用手机号验证码登录时&#xff0c;一般我们会生成6位数的随机验证码&#xff0c;并将验…

【iOS】——编译链接和动态链接器

前言 计算机语言分为机器语言&#xff1a;汇编语言&#xff0c;高级语言。 可以将高级语言分为两种&#xff1a;1&#xff0c;编译语言和解释型语言&#xff08;直译式语言&#xff09;。 编译型语言&#xff08;一次性翻译&#xff09; 编译型语言的程序只要经过编译器编译之…

Android 大屏外接显示器锁屏无触摸

一、大海捞针 1、首先查看log,发现异常log log表示主显示器即id 0的显示器有可交互窗口但是没有焦点,副显示器即id 4有焦点但是没有可交互窗口。猜想副显示器把主显示器的焦点抢走了,只需把焦点从副显示器挪回主显示器即可。 通过查看源代码知道上面这段log来自于InputDi…

filebeat,kafka,clickhouse,ClickVisual搭建轻量级日志平台

springboot集成链路追踪 springboot版本 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.6.3</version><relativePath/> <!-- lookup parent from…

【CSS in Depth 2 精译_019】3.2 CSS 的盒模型

当前内容所在位置&#xff08;可进入专栏查看其他译好的章节内容&#xff09; 第一章 层叠、优先级与继承&#xff08;已完结&#xff09; 1.1 层叠1.2 继承1.3 特殊值1.4 简写属性1.5 CSS 渐进式增强技术1.6 本章小结 第二章 相对单位&#xff08;已完结&#xff09; 2.1 相对…

【Qt安装与简易串口控制Arduino开发板小灯教程】

【Qt安装与简易串口控制Arduino开发板小灯教程】 1. 前言2. QT环境搭建3. 验证QT_Creator是否安装成功3.1 设计流程3. 2 上位机界面设计 4. 上位机逻辑代码编写4.1 添加串口库、包含串口相关头文件4.2 添加QSerialPort成员4.3 创建串口对象、搜索所有可用串口4.4 在编写“打开串…

微软成为PostgreSQL主要贡献者

微软对PostgreSQL贡献的很多新功能都来自于客户在使用微软Azure上的PostgreSQL管理实例数据库&#xff0c;所以这些新功能都来自于真实的客户需求 微软贡献的这些新功能都是比较实用的功能 在这里&#xff0c;【真实的客户需求】要突出一下&#xff0c;因为现在很多社区贡献者…

4. docker镜像、Dockerfile

docker镜像、Dockerfile 一、docker镜像1、镜像介绍2、镜像核心技术 二、Dockerfile定制镜像1、Dockerfile使用流程1.1 编写Dockerfile1.2、构建镜像1.3 创建容器测试镜像定制操作 2、Dockerfile常用指令 一、docker镜像 1、镜像介绍 分层的文件系统 优势&#xff1a;节省空间…

基于JAVA+SpringBoot+uniapp的心理小程序(小程序版本)

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、SpringCloud、Layui、Echarts图表、Nodejs、爬…

docker默认存储地址 var/lib/docker 满了,换个存储地址操作流程

1. 查看docker 存储地址 docker info如下 var/lib/docker2、查看内存大小 按需执行 df -h 找超过100M的大文件 find / -type f -size 100M -exec ls -lh {} \; df -Th /var/lib/docker 查找这个文件的容量 df -h 查找所有挂载点 du -hs /home/syy_temp/*1、df -h 2、sud…

2-38 基于matlab的蚁群算法优化无人机uav巡检

基于matlab的蚁群算法优化无人机uav巡检&#xff0c;巡检位置坐标可根据需求设置&#xff0c;从基地出发&#xff0c;返回基地&#xff0c;使得路径最小。可设置蚁群数量&#xff0c;信息素系数。输出最佳路线长度。程序已调通&#xff0c;可直接运行。 2-38 蚁群算法优化无人…

科普文:多线程如何使用CPU缓存?

一、前言 计算机的基础知识聊的比较少&#xff0c;但想要更好的理解多线程以及为后续多线程的介绍做铺垫&#xff0c;所以有必要单独开一篇来聊一下 CPU cache。 二、CPU 前面有一篇文章关于 CPU是如何进行计算 感兴趣的同学&#xff0c;可以先移步了解一下&#xff0c;不了…

[003-02-10].第10节:Docker环境下搭建Redis主从复制架构

我的博客大纲 我的后端学习大纲 我的Redis学习大纲 1.cluster&#xff08;集群&#xff09;模式-docker版 哈希槽分区进行亿级数据存储 1.1.面试题&#xff1a;1~2亿条数据需要缓存&#xff0c;请问如何设计这个存储案例 1.回答&#xff1a;单机单台100%不可能&#xff0c;肯…

细说MCU用定时器控制ADC采样频率的实现方法并通过Simulink查看串口输出波形

目录 一、硬件工程 二、建立Simulink模型 1.安装MATLAB和Simulink 2.建立Simulink模型 三、代码修改 1.修改回调函数 2.产看结果 3.完整的main.c 本文作者的文章 细说MCU用定时器控制ADC采样频率的实现方法-CSDN博客 https://wenchm.blog.csdn.net/article/details/…

270-VC709E 基于FMC接口的Virtex7 XC7VX690T PCIeX8 接口卡

一、板卡概述 本板卡基于Xilinx公司的FPGA XC7VX690T-FFG1761 芯片&#xff0c;支持PCIeX8、两组 64bit DDR3容量8GByte&#xff0c;HPC的FMC连接器&#xff0c;板卡支持各种FMC子卡扩展。软件支持windows&#xff0c;Linux操作系统。 二、功能和技术指标&#xff1a; 板卡功…

Getx学习笔记之中间件鉴权

目录 前言 一、实现步骤 1.添加依赖 2.创建鉴权中间件 3.定义路由 4.设置初始路由 5.模拟登陆状态 二、Getx鉴权步骤总结 三、本文demo示例 四、参考文章 前言 在 Flutter 中&#xff0c;使用 GetX 可以很方便地实现中间件鉴权&#xff08;Authentication&#xff09…