【Java EE初阶九】多线程进阶一(锁策略)

前言

        锁的策略:加锁过程中,即处理冲突的过程中,需要涉及到的一些不同的处理方式(此处锁策略并不是java独有的),本篇内容主要是讲解一下关于锁的相关知识点;

1. 关于锁的分组

1.1 第一组:乐观锁和悲观锁

------>这是两种不同的锁的实现方式

        乐观锁:在加锁之前,预估当前出现锁冲突的概率不大,因此在进行加锁的时候不会做太多的工作。加锁过程中做的事情比较少,加锁的速度可能更快,但是更容易引入一些其他的问题(即可能会消耗更多的cpu资源)

        悲观锁:在加锁之前,预估当前出现锁冲突的概率比较大,因此在进行加锁的时候会做更多的工作,因为做的事情更多,所以加锁的速度回更慢,但是整个过程中不容易出现其他的问题。

1.2 第二组:轻量级锁和重量级锁

        轻量级锁,加锁的开销更小,加锁的速度更快--->轻量级锁,一般都是乐观锁

        重量级锁,加锁的开销更大,加锁的速度更慢--->轻量级锁,一般都是悲观锁

        轻量重量是加锁之后对结果的评价,悲观乐观,是加锁之前,对未来的事情进行的预估,根据整体来说,这两种角度,描述的都是同一个事情;

1.3 第三组:自旋锁和挂起等待锁

        自旋锁:是轻量级锁的一种典型实现;加锁的时候,如果加锁失败,不会就此阻塞放弃cpu,而是相当于添加一个while循环,不停的进行锁竞争,这次锁竞争失败了就再次进入循环竞争锁,直到加锁成功,才退出循环。

        这种反复快速的执行,就称为 “自旋”,故此自旋锁也是乐观锁,使用自旋锁的前提是预期锁冲突概率不大,只要其他线程释放了锁,它就能第一时间加锁成功;但如果有很多线程要加锁,就必要使用自旋锁了,因为会浪费cpu资源。

        等待挂起锁:是重量级锁的一种典型体现,也是悲观锁;加锁的时候,如果加锁失败,就会等待一段时间,这段时间它不会去进行加锁操作和别的线程锁竞争,故此就能把一些cpu资源让出来,被让出来的这些cpu资源可以干一些其他事情,等一段时间过后,会再次尝试加锁,如果失败还是重复以上工作,直到最后成功拿到锁;

        当等待挂起的时候,会有内核调度器介入,所以就需要完成的操作就多了,从而要获取锁的时间花费也更多一些。

Q:java中的synchronized算以上锁的哪种情况?

A:synchronized具有自适应能力,且synchronized在某些情况下是乐观锁,轻量级锁,自旋锁;但是在某些情况下时悲观锁,重量级锁,等待挂起锁,当然系统内部会自动的评估当前锁冲突的激烈程度;

        如果当前锁冲突的激烈程度不大,就处于乐观锁\轻量级锁\自旋锁;

        如果当前锁冲突的激烈程度很大,就处悲观锁锁\重量级锁\等待挂起锁;

1.4 第四组: 普通互斥锁和读写锁

        普通互斥锁:类似synchronized这种,操作涉及到加锁、解锁

        读写锁:这里加锁的情况分为两种:加读锁,加写锁

        读锁和读锁之间,不会出现锁冲突(不会阻塞);写锁和写锁之间,会涉出现锁冲突(会阻塞);读锁和写锁之间,会出现锁冲突(会阻塞);当一个线程加读锁时,另一个线程只能读,不能写;当一个线程加写锁时,另一个线程不能写,也不能读

Q:为啥要引入读写锁?

A:     如果两个线程读,本身就是线程安全的,不需要进行互斥;

        如果使用synchronized这种方式加锁,两个线程读与会产生互斥(阻塞),对于性能有一定的损耗,完全给读操作不加锁,就怕连个线程一个是读线程,一个是写线程,这样更加会涉及锁冲突;如上所述,读写锁能很好地解决上述的问题

         java标准库中,也提供了专门的类,来实现读写锁(读写锁本质上还是系统提供的读写锁,系统提供api,jvm中将api进行封装给我们使用),注意synchornized不是读写锁;

1.5 第五组:公平锁和非公平锁

        此处的公平即遵循先来后到的规则才是公平;

        公平锁:如果线程和线程之间,锁竞争的时间大小不一样,按照锁竞争时间久的线程先拿到锁,有先后顺序(先来后到的意思)

       非公平锁:线程和线程之间没有拿锁顺序,随机调度,各凭各的本事拿锁。

        这里使用公平锁,就能很好的解决线程饿死的这一问题。而要想实现公平锁,就需要引入额外的数据结构(引入队列,记录每个线程的先后顺序),才能实现公平锁。

1.6 第六组:可重入锁和不可重入锁

        可重入锁如synchronized,加锁一段代码,锁里面可以再进行一次加锁,锁里面可以嵌套多个锁,里面是用计数器这种方式对加锁的数量进行计数,并判断是否解锁,是可重入锁。

        不可重入锁系统自带的锁,不能连续加锁两次。

1.7 synchronized和Linux基于特定锁的简单比较

        下面来说明一下synchronized和系统自带的锁的具体锁;
synchronized:乐观锁 / 悲观锁自适应

                        轻量级锁 / 重量级锁自适应

                        自旋锁 / 挂起等待所自适应

                        不是读写锁

                        非公平锁

                        可重入锁

linux提供的mutex(系统自带的锁):          

                        悲观锁

                        重量级锁

                        挂起等待所

                        不是读写锁

                        非公平锁

                        不可重入锁

 

2. 细说synchronized的自适应

        Synchronized的加锁过程,自适应如下所述:

        当线程执行到synchronized的时候,如果这个对象处于未加锁的状态,就会经历一下的过程:

2.1 锁升级

        1、偏向锁阶段

        核心思想:“懒汉模式”,就是能不加锁,就不加锁;所谓的偏向锁,并不是真的加锁了,只是做了个非常轻量的标记。

        一旦有线程来竞争这个锁,持有偏向锁的线程能第一时间拿到这个锁,如果没有其他线程竞争锁,下次还是拿到锁的线程大概率还是持有偏向锁的线程。

        总的来说,当有锁竞争的情况下,偏向锁没有提高效率,在没有锁竞争的情况下,偏向锁就能大幅度的提高效率了。

        这个标记是锁里面的一个属性,每个锁都有自己的标记,当锁对象首次加锁是,没有涉及到锁竞争,这个阶段就是偏向锁阶段,一旦涉及到锁竞争,就会升级成轻量级锁阶段。

        2、轻量级锁阶段

        此处的轻量级锁就是通过自旋的方式实现的。假设有锁竞争,但不多的时候,就会处于轻量级锁阶段,它的优势:当其他线程释放锁了,处于轻量级锁阶段的线程能第一时间拿到锁;劣势:比较消耗cpu资源,因为是自旋的方式实现的,会有个循环一直尝试拿锁。

        当线程多了,轻量级锁就不合适了,每个线程都循环尝试拿锁,但如果已经有线程拿到锁了,其他线程要阻塞等待,但等待的这过程是会有循环不断的尝试拿锁,这里消耗的cpu资源就很多了。这时,就会从轻量级锁阶段升级成重量级锁阶段。

        3、重量级锁阶段
        此处的重量级锁是用挂起等待的方式实现的,当有很多线程同时去竞争这个锁时,有个线程拿到锁了,其他线程没拿到,就会阻塞等待,等待一段时间再去尝试拿锁,不成功再阻塞等待一段时间,循环以上步骤,如此就会让出cpu的资源,可以利用这些cpu资源干一些其他的事。

        注意:当前版本此处只能升级,不能降级。

2.2 锁消除 

        锁消除是synchronized的内部优化;

        有时候,有些代码可以一眼看上去就不用加锁,但是代码加锁了,这时候,编译器就会把这个锁给消除掉,毕竟加锁操作也是要消耗一些硬件资源的。

        注意:锁消除和偏向锁的区别

        锁消除:针对能够一眼就看出不涉及到线程安全问题的代码,编译器能够把锁给消除掉。

        偏向锁:是已经运行代码了,才知道没有锁竞争。

2.3 锁粗化

        通常情况下,我们更偏好于让锁更细一些,这样更有利于并发编程的时候解决线程安全问题;但有时候,让锁更粗写能提高效率,会希望锁粗点。

        锁粗化:把多个细粒度的锁合并成一个粗粒度的锁。如下图所示:

        一段代码中,频繁的加锁解锁,肯定会消耗更多的硬件资源,但是如果能把一段代码的多个加锁、解锁操作,优化成只有一次加锁、解锁,这样也能提高效率。

ps:关于多线程中锁策略的内容就到这里了,如果对你有所帮助的话,就请一键三连哦!!!

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

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

相关文章

动态内存管理:malloc free

//——————1.动态内存管理(内存空间) 共四个函数:malloc free calloc realloc 1. 为什么存在动态内存分配 我们已经掌握的内存开辟方式有: int val 20;//在栈空间上开辟四个字节 char arr[10] { 0 };//在栈空间上开…

一、Qt介绍

一、Qt介绍 1、介绍 Qt是一套程序开发库,但是与MFC(依赖于Windows API)不同,Qt是跨平台开发库。 Qt获取:[Qt下载地址](https://download.qt.io/archive/qt/)2、Qt安装 QtMinGWSourcesQt ChartsQt Data Visualizatio…

如何在Android Glide中结合使用CenterCrop和自定义圆角变换(图片部分圆角矩形)

如何在Android Glide中结合使用CenterCrop和自定义圆角变换(图片部分圆角矩形) 在Android开发中,使用Glide加载图片时,我们经常需要对图片进行特定的处理,比如裁剪和圆角变换,特别是一些设计稿,…

cpufreq子系统

cpufreq是linux上负责实现动态调频的关键,这篇笔记总结了linux内核cpufreq子系统的关键实现(Linux 3.18.140)。 概述 借用一张网络上的图片来看cpufreq子系统的整体结构: 用户态接口:cpufreq通过sysfs向用户态暴露接…

Java知识点:泛型、类加载器、内部类、可变参数

文章目录 1、this关键字2、泛型2.1 泛型介绍2.2 泛型分类定义2.2.1 泛型类2.2.2 泛型方法2.2.3 泛型接口 2.3 泛型通配符 3、可变参数4、日志4.1 使用步骤4.2 日志级别4.3 配置文件 5、类加载器5.1 类加载器5.2 类加载的完整过程5.2.1 类加载时机5.2.2 类加载过程 5.3 类加载的…

【开源项目】超经典开源项目实景三维数字孪生智慧工厂

数字孪生工厂,以模型驱动的自动化,与数据驱动的人工智能技术紧密融合与协同,实现机器、工件与组件间全面的和点对点的数据通信。飞渡科技基于自研DTS平台,将物联网IOT、人工智能、大数据、云计算等技术应用于工厂,实现…

期货日数据维护与使用_日数据维护_模块运行演示

写在前面: 本文默认已经创建了项目,如果不知道如何创建一个空项目的,请参看以下两篇博文 PyQt5将项目搬到一个新的虚拟环境中 https://blog.csdn.net/m0_37967652/article/details/122625280 python_PyQt5开发工具结构基础 https://blog.cs…

TMC4671闭环调试步进、伺服、音圈、永磁、无刷电机

一、IDE 连接开发板 下 面 讲 解 IDE 和 开 发 板 连 接 的 详 细 操 作 。 这 里 我 们 选 择 用 主 控 板 TMC671-EVALTMC6200-EVAL 开发板做讲解。其它型号的开发板也是大同小异 的操作步骤。 1.首先我连接好开发板,并给开发板上电 连接好的开发板如下…

【激活函数】SELU 激活函数

1、介绍 SELU (Scaled Exponential Linear Unit) SELU是对ELU激活函数的改进,通过引入自动标准化机制,使得神经网络的隐藏层在训练过程中可以自动地保持输出的均值和方差接近于1。 # 定义 SELU 激活函数 def selu(x, alpha1.67326, lambda_1.0507):retu…

【Python百宝箱】数据清洗艺术:Python库助力打磨完美数据

数据清洗与预处理:Python库大揭秘 前言 在数据科学领域,数据清洗和预处理是构建可靠模型的关键步骤。本文深入探讨了一系列强大的Python库,它们在处理重复数据、字符串匹配、数据整理以及降维等方面发挥着重要作用。通过学习这些库&#xf…

单片机相关知识点

在STM32上运行FreeRTOS,十分简练的小文章FreeRTOS(STM32CubeMX)_cubemx freertos-CSDN博客 STM32CubeMX转Keil使用STM32CubeMX生成Keil工程并完成流水灯-CSDN博客

unity PDFRender Curved UI3.3

【PDF】PDFRender 链接:https://pan.baidu.com/s/1wSlmfiWTAHZKqEESxuMH6Q 提取码:csdn 【曲面ui】 Curved UI3.3 链接:https://pan.baidu.com/s/1uNZySJTW0-pPwi2FTE6fgA 提取码:csdn

【unity小技巧】FPS游戏实现相机的震动、后坐力和偏移

最终效果 文章目录 最终效果前言相机的震动实现后坐力和偏移相机震动相机震动脚本换弹节点震动 武器射击后退效果完结 前言 关于后坐力之前其实已经分享了一个:FPS游戏后坐力制作思路 但是实现起来比较复杂,如果你只是想要简单的实现,可以看…

Linux ssh 实现远程免密登录

一、背景 我搭建了一个 zookeeper 集群,写了一个 shell 脚本来控制集群的启动和关闭,但是我发现每次我执行 shell 脚本的时候,都需要我输入各个服务器的密码才可以运行,感觉很麻烦。shell 脚本里面连接其他服务器用的就是 ssh 的方…

Linux安装JDK和Maven并配置环境变量

文章目录 一、安装JDK并配置环境变量二、安装maven并配置环境变量 一、安装JDK并配置环境变量 将JDK的安装包上传到Linux系统的usr/local目录 使用xftp上传文件 解压JDK的压缩包 xshell连接到云主机 [roottheo ~]# cd /usr/local[roottheo local]# ls aegis apache-tomcat-…

equals()方法和“==”运算符

equals()equals()方法和“”运算符比较 回到顶部 equals() 超类Object中有这个equals()方法,该方法主要用于比较两个对象是否相等。该方法的源码如下: public boolean equals(Object obj) {return (this obj);} 我们知道所有的对象都拥有标识(内存…

使用LVM分区方式安装Manjaro发行版

使用LVM分区方式安装Manjaro发行版 为什么单独介绍LVM方式呢? 主要是由于使用系统的图形安装工具创建卷组会出问题,会导致图形安装工具直接挂掉,唯一的方法是提前手动创建好卷组。 GPT分区表 分区表有: MBR(主引导记录分区表)…

【华为OD真题 Python】两数之和绝对值最小

文章目录 题目描述输入描述输出描述示例1输入输出说明代码实现题目描述 给定一个从小到大的有序整数序列(存在正整数和负整数)数组 nums ,请你在该数组中找出两个数,其和的绝对值(|nums[x]+nums[y]|)为最小值,并返回这个绝对值。 每种输入只会对应一个答案。但是,数组中…

性能优化-OpenMP概述(一)-宏观全面理解OpenMP

本文旨在从宏观角度来介绍OpenMP的原理、编程模型、以及在各个领域的应用、使用、希望读者能够从本文整体上了解OpenMP。 🎬个人简介:一个全栈工程师的升级之路! 📋个人专栏:高性能(HPC)开发基础…

【算法】链表-20240105

这里写目录标题 一、LCR 023. 相交链表二、142. 环形链表 II 一、LCR 023. 相交链表 给定两个单链表的头节点 headA 和 headB ,请找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。 提示: listA 中节点数目为 m list…