秋招Java后端开发冲刺——并发篇2(JMM与锁机制)

本文对Java的内存管理模型、volatile关键字和锁机制进行详细阐述,包括synchronized关键字、Lock接口及其实现类ReentrantLock、AQS等的实现原理和常见方法。

一、JMM(Java内存模型)

1. 介绍
JMM定义了共享内存多线程程序读写操作的行为规范,通过这些规则来规范对内存的读写操作从而保证指令的正确性。
2. 模型
在这里插入图片描述

  • JMM把内存分为两块,一块是私有线程的工作区域(工作内存),一块是所有线程的共享区域(主内存)
  • 线程跟线程之间是相互隔离,线程跟线程交互需要通过主内存

二、Volatile关键字

1. 介绍

  • Volatile关键字主要用于修饰成员变量(实例变量或类变量),使得对该变量的读写操作直接与主内存交互,而不是通过线程本地的工作内存进行缓存。
  • Volatile关键字提供了一种轻量级的同步机制,用于保证多线程环境下数据的可见性(但不能保证数据的原子性,Synchronized关键字既保证可见性,又保证原子性)

2. Volatile禁止指令重排
(1)指令重排

指令重排是一种编译器和处理器优化技术,它改变指令执行的顺序,以提高性能。在单线程环境下,这种优化不会影响程序的正确性。但是在多线程环境下,指令重排可能导致意想不到的结果,破坏程序的正确性。

(2)Volatile如何禁止指令重排

  • 通过在读写 volatile 变量时插入内存屏障(Memory Barriers)来实现
    • 写屏障(Write Barrier):确保在写 volatile 变量之前的所有写操作在内存中完成。
    • 读屏障(Read Barrier):确保在读 volatile 变量之后的所有读操作在内存中完成
  • volatile关键字一般加在写变量最后一个,读变量第一个。
class Example {private volatile boolean flag = false;private int a = 0;public void writer() {a = 1;           // 1flag = true;     // 2}public void reader() {if (flag) {      // 3int i = a;   // 4}}
}

上述示例中:

  • 在writer方法中,写入a = 1(1)操作不能被重排序到写入flag = true(2)之后。因为flag是volatile,在写flag的时候会插入一个StoreStore屏障,确保在flag被写入之前,所有之前的写操作都完成了。
  • 在reader方法中,读取flag(3)操作不能被重排序到读取a(4)之前。因为flag是volatile,在读flag的时候会插入一个LoadLoad屏障,确保在flag被读取之后,所有之前的读操作都完成了。

三、锁

1. 悲观锁和乐观锁
(1)悲观锁:认为访问共享资源时一定会发生线程安全问题,因此在访问时必须加锁。
(2)乐观锁:认为访问共享资源时不一定会发生线程安全问题,因此不会加锁,当提交修改的时候去验证对应的资源(也就是数据)是否被其它线程修改即可。
(3)二者区别

特性悲观锁乐观锁
锁定机制操作数据前先加锁,其他线程无法操作操作数据时不加锁,提交时检查冲突
使用场景适用于写多读少,冲突概率高的场景适用于读多写少,冲突概率低的场景
性能由于频繁加锁和释放锁,性能较低不加锁,性能较高
实现方式使用数据库锁机制(如行锁、表锁)使用版本号或时间戳进行冲突检测
数据一致性一致性强,通过锁机制防止并发修改一致性依赖于冲突检测和重试机制
并发性并发性差,锁定会阻塞其他线程并发性好,线程可以同时进行操作
死锁风险存在死锁风险不存在死锁风险
典型应用行锁、表锁、页锁和意向锁(数据库)、ReentrantLock等版本控制系统、CAS(Compare and Swap)操作

2. synchronized 关键字
(1)介绍
synchronized 关键字是一种对象锁,采用互斥的方式让同一时刻至多只有一个线程能持有对象锁。
(2)底层实现原理
① Monitor(对象监视器)

  • Monitor(监视器)是一种同步机制,用于管理多线程之间的互斥访问和并发控制。Monitor是JVM提供的,但是是基于c++实现的。
  • Monitor的结构
    • Owner:存储当前获取锁的线程的,只能有一个线程可以获取
    • EntryList:关联没有抢到锁的线程,处于Blocked状态的线程
    • WaitSet:关联调用了wait方法的线程,处于Waiting状态的线程

在这里插入图片描述
② synchronized关键字实现原理
线程获得锁需要使用对象(锁)关联monitor监视器,即获得monitor的持有权。

(3)使用方法

  • 同步方法:当一个线程执行同步方法时,它会自动获得该方法所属对象的锁。其他线程在获得该锁之前无法执行该方法。
public class SynchronizedExample {private int count = 0;public synchronized void increment() {count++;}public synchronized int getCount() {return count;}
}
  • 同步块:同步块用于在一个方法内同步部分代码,提供了比同步方法更细粒度的控制。
public class SynchronizedBlockExample {private final Object lock = new Object();private int count = 0;public void increment() {synchronized (lock) {count++;}}public int getCount() {synchronized (lock) {return count;}}
}
  • 静态同步方法:静态同步方法锁定的是类对象,对于所有该类的实例都有效。
public class StaticSynchronizedExample {private static int count = 0;public static synchronized void increment() {count++;}public static synchronized int getCount() {return count;}
}

3. synchronized锁升级

Monitor实现的锁属于重量级锁,因为monitor是使用c++实现的,里面涉及到了用户态和内核态的切换、进程的上下文切换,成本较高,性能比较低。在JDK 1.6引入了两种新型锁机制:偏向锁轻量级锁

(1)对象的内存结构
在这里插入图片描述
:填充为8的整数倍是方便字节对齐提高程序运行效率 ,而且对于压缩指针也更加的方便。

(2)轻量级锁
在无竞争的情况下,轻量级锁使用CAS操作来实现锁的获取和释放,提高运行效率。
① 加锁流程

  • 在线程栈中创建一个Lock Record,将其obj字段指向锁对象。
  • 通过CAS指令将Lock Record的地址存储在对象头的mark word中,如果对象处于无锁状态则修改成功,代表该线程获得了轻量级锁。
  • 如果是当前线程已经持有该锁了,代表这是一次锁重入。设置Lock Record第一部分为null,起到了一个重入计数器的作用。(仍需CAS操作)
  • 如果CAS修改失败,说明发生了竞争,需要膨胀为重量级锁。

② 解锁过程

  • 遍历线程栈,找到所有obj字段等于当前锁对象的Lock Record。
  • 如果Lock Record的Mark Word为null,代表这是一次重入,将obj设置为null后continue。
  • 如果Lock Record的 Mark Word不为null,则利用CAS指令将对象头的mark word恢复成为无锁状态。如果失败则膨胀为重量级锁。

(3)偏向锁
与轻量级锁不同的是,偏向锁只第一次获得锁时使用 CAS 将线程 ID 设置到对象的 Mark Word 头,之后该线程再获取锁时,只需要判断锁对象mark word中是否是自己的线程id即可。

:轻量级锁和偏向锁都是在没有竞争的情况下使用,一旦发生了竞争会立刻升级为重量级锁。

4. Lock接口:ReentrantLock

Lock接口提供了一种比传统的同步块和方法更灵活的锁机制。Lock 接口有多种实现,其中最常用的是 ReentrantLock。

(1)Lock接口常见实现类

实现类描述
ReentrantLock可重入的互斥锁,具有与使用 synchronized 方法和语句相同的基本行为和语义,但功能更强大。
ReentrantReadWriteLock.ReadLock读锁,可被多个读线程同时持有,只要没有写线程持有写锁。
ReentrantReadWriteLock.WriteLock写锁,独占锁,只有写线程可以持有,读线程和其他写线程都被阻塞。
StampedLock一种新的读写锁实现,支持乐观读锁和写锁的互斥锁操作,提高并发性和性能。
LockSupport提供了基本的线程阻塞原语,用于创建锁和其他同步工具。

(2)ReentrantLock
① ReentrantLock是一个可重入且独占式的锁,还支持轮询、超时、中断、公平锁和非公平锁等。
② 特点

  • 可重入性:一个线程可以多次获得该锁,而不会导致死锁。这意味着同一个线程可以进入锁保护的代码块多次,必须确保在离开代码块时相应的次数解锁。
  • 公平性:可以选择公平锁或者非公平锁。公平锁按照线程请求的顺序获取锁,非公平锁则没有这种顺序,可能导致某些线程长时间等待。
  • 灵活性:提供了比 synchronized 块更灵活的锁获取方式,如可中断的锁获取、超时的锁获取。

③ 实现原理

  • ReentrantLock使用CAS+AQS来实现,使用用一个整数state表示锁的状态,使用一个先进先出的队列(CLH)管理被阻塞的线程。
  • 当线程需要获得锁时,使用CAS操作修改锁的状态,修改成功则获得锁;修改失败则加入CLH队列管理。

④ 常用方法

方法描述
lock()获取锁。如果锁不可用,则当前线程将被禁用以进行线程调度,并处于休眠状态,直到获得锁。
unlock()释放锁。
tryLock()尝试获取锁。如果锁在调用时未被另一个线程持有,则获取锁并立即返回 true;否则返回 false
tryLock(long time, TimeUnit unit)尝试在给定的等待时间内获取锁。如果锁在给定时间内可用,则获取锁并返回 true;否则返回 false
lockInterruptibly()如果当前线程未被中断,则获取锁。如果锁不可用,则当前线程出于线程调度目的将被禁用并处于休眠状态,直到发生以下两种情况之一:锁由当前线程获得;或者其他某个线程中断当前线程。
newCondition()返回一个绑定到此 Lock 实例的新 Condition 实例。

5. synchronized与ReentrantLock的比较

特性synchronizedReentrantLock
锁类型隐式锁显式锁
获取锁的方式自动需要显式调用 lock() 方法
释放锁的方式自动需要显式调用 unlock() 方法
可重入性
公平锁支持是(通过构造函数可以指定)
条件变量支持是(通过 newCondition() 方法)
获取锁中断是(通过 lockInterruptibly() 方法)
超时锁获取是(通过 tryLock(long time, TimeUnit unit) 方法)
性能开销较低(JVM 内部优化)较高
锁的范围代码块或方法灵活,可锁代码块
代码复杂度较高
推荐使用场景简单的同步需求复杂的同步需求,如需要公平锁、超时等

6. AQS(Abstract Queued Synchronizer,抽象队列同步器)
(1)介绍
AQS是Java 并发包 (java.util.concurrent.locks) 中的一个核心框架,是构建各种锁和同步组件的基础框架。
(2)实现原理

  • AQS 使用一个 int 类型的变量 state 表示同步状态。
  • AQS 内部维护了一个 FIFO 队列,用于管理获取锁失败的线程。线程在争夺锁失败后会被封装成队列节点(Node)并加入到等待队列中。
  • 队列中每个节点包含线程引用和状态标志

(3)常用方法

方法描述
acquire(int arg)独占模式获取锁,如果获取失败则进入等待队列。
release(int arg)独占模式释放锁,如果释放成功则唤醒等待队列中的下一个节点。
acquireShared(int arg)共享模式获取锁,如果获取失败则进入等待队列。
releaseShared(int arg)共享模式释放锁,如果释放成功则唤醒等待队列中的下一个节点。
addWaiter(Node mode)将当前线程封装成节点并加入等待队列的尾部。
unparkSuccessor(Node node)唤醒等待队列中的下一个节点。

(4)常见实现类

实现类描述
ReentrantLock可重入独占锁。允许线程重复获取同一把锁。
ReentrantReadWriteLock可重入读写锁。支持多个线程同时读取数据,但写操作是互斥的。
Semaphore信号量。控制同时访问特定资源的线程数。
CountDownLatch倒计时闩。一个或多个线程等待其他线程完成操作后再继续执行。
CyclicBarrier循环栅栏。一组线程相互等待,达到同步点后再继续执行。
Phaser阶段器。管理多个参与者线程的同步,支持动态注册和注销。
StampedLock读写锁的改进版本,支持乐观读取和写锁。
Condition条件变量。与锁配合使用,允许线程等待某个条件变为真。

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

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

相关文章

【电源专题】DC-DC电路设计为什么一般只考虑电感DCR而不考虑Q值呢?

什么是电感器(线圈)的Q值? Q值是表示电感器质量的参数。Q是Quality Factor(质量系数)的简称。线圈会顺利流过直流电流,但会对交流电流产生电阻。这称为感抗,交流频率越高则越大。 此外,绕组虽是导体…

MTK6769芯片性能参数_MT6769规格书_datasheet

联发科MT6769处理器采用了台积电12nm工艺。它具有8核CPU,采用2Cortex A75 2.0GHz 6Cortex A55 1.7GHz的构架。该处理器搭载了Mali-G52 MC2 GPU,运行速度高达820MHz,能够提供出色的图形处理性能。此外,MT6769还提供高达8GB的快速L…

PDM与ERP物料编码技术在产品设计中的区别与应用

01 概 述 产品是企业赖以生存的基础,产品数据是企业最基本的也是最重要的数据,产品数据存在于产品设计、采购、生产、销售、服务、库存管理等全过程中。通过对产品设计数据进行编码,并增加采购、库存、生产、制造等属性信息,可以…

Kamailio-命令行指令kamctl与kamcmd

前文也有提到几种指令的用处,与web页面相比,它就是更原始、面向运维的,正常如果有管理页面也需要使用到: kamailio - SIP 服务器脚本kamdbctl - 创建和管理数据库的脚本,比如你使用MySQL作为其存储时就需要使用到这个…

阳光倒灌试验太阳辐射系统日光模拟器

太阳光模拟器概述 太阳光模拟器是一种能在实验室环境下模拟太阳光照射特性的设备,广泛应用于材料科学、能源研究、环境科学等领域。通过模拟太阳光的光谱分布和辐射强度,太阳光模拟器可以为科研和工业提供稳定且可重复的光照条件,进而对材料…

航空数据管控系统-①项目准备阶段:任务2:项目技术预研(技术架构)

任务描述 掌握项目的总体功能,及实现流程。预习项目中所使用到的技术和知识点。 任务指导 一、项目效果展示 图1-数据统计大屏页面 图2-航空实时监控页面 二、项目架构 1、总体架构: 2、技术架构 技术清单: 功能 组件 说明 消息中间件…

用for语句实现九九乘法表

① #define _CRT_SECURE_NO_WARNINGS #include <stdio.h>int main() {for (int i 1; i < 9; i){for (int j 1; j < i; j){printf("%d*%d%d\t", j, i, i * j);}printf("\n");}return 0; } ② #define _CRT_SECURE_NO_WARNINGS #include &…

数据分析入门指南:从基础概念到实际应用(一)

随着数字化时代的来临&#xff0c;数据分析在企业的日常运营中扮演着越来越重要的角色。从感知型企业到数据应用系统的演进&#xff0c;数据驱动的业务、智能优化的业务以及数智化转型成为了企业追求的目标。在这一过程中&#xff0c;数据分析不仅是技术的运用&#xff0c;更是…

Java项目:基于SSM框架实现的班主任助理管理系统【ssm+B/S架构+源码+数据库+开题报告+毕业论文】

一、项目简介 本项目是一套基于SSM框架实现的班主任助理管理系统 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 该系统功能完善、界面美观、操作简单、功…

新手必学:TikTok视频标签的使用方法

想让你的TikTok视频火起来&#xff0c;就得用对标签。标签能帮你的作品被更多人看到&#xff0c;也更有利于推广&#xff0c;可以为品牌增加曝光度、吸引更多观众、提高转化率和借势热门话题。那么应该如何选择标签并使用标签呢&#xff0c;看完这篇分享你或许会有所启发&#…

C# 快速排序算法的详细讲解

目录 一、前言 二、例子 三、快速排序算法图片讲解 四、快速排序算法代码 五、纯净代码 一、前言 用比较好懂的方式讲一下快速排序算法。 二、例子 如果我有一堆钱&#xff0c;想数清楚&#xff0c;最快的方案是什么&#xff1f; 图1 一堆钱 答&#xff1a;先分类&…

【C语言】bool 关键字

在C语言中&#xff0c;bool类型用于表示布尔值&#xff0c;即真或假。C语言本身在标准库中并未提供布尔类型&#xff0c;直到C99标准引入了stdbool.h头文件。该头文件定义了bool类型&#xff0c;以及两个常量&#xff1a;true和false。在此之前&#xff0c;通常使用整数来表示布…

PHP花涧订购系统-计算机毕业设计源码00332

摘 要 近年来&#xff0c;电子商务的快速发展引起了行业和学术界的高度关注。花涧订购系统旨在为用户提供一个简单、高效、便捷的花卉购物体验&#xff0c;它不仅要求用户清晰地查看所需信息&#xff0c;而且还要求界面设计精美&#xff0c;使得功能与页面完美融合&#xff0c;…

告别烦人的捆绑软件!一键获取真正纯净系统!

很多用户反映自己下载到的纯净版系统&#xff0c;总是携带着各种各样的捆绑软件&#xff0c;特别影响自己的操作体验感&#xff0c;想知道哪里才有真正纯净的操作系统&#xff1f;以下系统之家小编给大家分享做到真正纯净的电脑操作系统。这些系统经过优化&#xff0c;去除乱七…

CSS 背景效果

目录 一、CSS背景属性 二、准备工作 三、background-color 四、background-image 五、background-repeat 六、background-position 七、background-size 八、background-attachment 九、background-clip 十、background-origin 十一、background 一、CSS背景属性 在…

零障碍入门:SSH免密登录与Hadoop生态系统的完美搭档【实训Day02】

一、 SSH免密登录配置 1 生成公钥和秘钥(在hadoop101上) # su star # cd /home/star/.ssh # ssh-keygen -t rsa 2 公钥和私钥 公钥id_rsa.pub 私钥id_rsa 3 将公钥拷贝到目标机器上(在hadoop101上) # ssh-copy-id hadoop101 # ssh-copy-id hadoop102 # ssh-co…

保存huggingface缓存中AI模型(从本地加载AI模型数据)

在github下拉项目后,首次运行时会下拉一堆模型数据&#xff0c;默认是保存在缓存的&#xff0c;如果你的系统盘空间快满的时候就会被系统清理掉&#xff0c;每次运行又重新下拉一次&#xff0c;特别麻烦。 默认下载的缓存路径如下&#xff1a;C:\Users\用户名\.cache\huggingf…

数据库管理系统中的磁盘、文件、页和记录管理

1. 引言 数据库管理系统&#xff08;DBMS&#xff09;是一个复杂的软件系统&#xff0c;用于管理和操作数据库中的数据。DBMS需要有效地在磁盘和内存之间组织和管理数据&#xff0c;以确保高效的数据存储和检索。本文将详细介绍DBMS中关于磁盘、文件、页和记录的管理&#xff…

GD32实战项目-app inventor-BLE低功耗DX-BT24蓝牙上位机制作-文末有关于生成的软件闪退或者卡死问题的解决

本文章基于兆易创新GD32 MCU所提供的2.2.4版本库函数开发 后续项目主要在下面该专栏中发布&#xff1a; 手把手教你嵌入式国产化_不及你的温柔的博客-CSDN博客 感兴趣的点个关注收藏一下吧! 电机驱动开发可以跳转&#xff1a; 手把手教你嵌入式国产化-实战项目-无刷电机驱动&am…

zabbix 配置企业微信告警

1、申请一个企业微信&#xff0c; 官网链接 2、群内申请一个机器人 下载电脑版企业微信&#xff0c;登录后&#xff0c;在要接收群消息的群里&#xff0c;点击右上角三个点&#xff0c;添加机器人后&#xff0c;保存机器人的webhook地址 上传应用logo&#xff0c;填写应用名称…