JavaEE初阶Day 13:多线程(11)

目录

  • Day 13:多线程(11)
    • 常见的锁策略
      • 1. 悲观锁 vs 乐观锁
      • 2. 重量级锁 vs 轻量级锁
      • 3. 自旋锁 vs 挂起等待锁
      • 4. 可重入锁 vs 不可重入锁
      • 5. 公平锁 vs 非公平锁
      • 6. 互斥锁 vs 读写锁
    • synchronized实现原理
      • 1. 锁升级
      • 2. 锁消除
      • 3. 锁粗化
    • CAS

Day 13:多线程(11)

常见的锁策略

锁策略可以理解为,这把锁在加锁/解锁/遇到锁冲突的时候都会怎么做

并非局限于Java中,其他编程语言,其他的系统级别的组件,但凡涉及到锁,都和锁策略有关系

1. 悲观锁 vs 乐观锁

加锁的时候,预测当前锁冲突的概率是大还是小

  • 预测当前锁冲突概率大,后续要做的工作往往就会更多,加锁的开销就更大(时间、系统资源),此时采用悲观锁
  • 预测当前锁冲突概率小,后续要做的工作往往就会更少,加锁的开销就更小(时间、系统资源),此时采用乐观锁

Java中的synchronized既是乐观锁也是悲观锁,支持自适应,能够自动的统计出当前锁冲突的次数,进行判定当前锁冲突的概率高低

  • 当冲突概率低的时候,按照乐观锁的方式来执行(速度更快)
  • 当冲突概率高的时候,升级为悲观锁的方式执行(做的工作更多)

悲观锁往往是要通过内核来完成一些操作的,要做的工作就多

乐观锁往往是纯用户态的一些操作,要做的工作就少

2. 重量级锁 vs 轻量级锁

一般来说,悲观锁往往就是重量级锁;乐观锁往往就是轻量级锁

  • 加锁过程做的事情多,重量
  • 加锁过程做的事情少,轻量

3. 自旋锁 vs 挂起等待锁

  • 自旋锁是轻量级锁的一种典型实现方式
//伪代码
void lock() {
	while(true) {
		if(锁是否被占用) {
			continue;
		}
		获取到锁
		break;
	}
}

cpu在空转忙等,消耗了更多的CPU资源,但是一旦锁被释放,就能第一时间拿到锁

  • 挂起等待锁是重量级锁的一种典型实现方式

    • 借助系统中的线程调度机制,当尝试加锁,并且锁被占用了,出现锁冲突,就会让当前这个尝试加锁的线程被挂起(阻塞状态)
    • 此时这个线程就不会参与调度了,直到这个锁被释放,然后系统才能唤醒这个线程,去尝试重新获取锁,拿到锁的速度更慢,节省CPU,消耗的时间更长,一旦线程被阻塞了,什么时候被唤醒,这个过程是不可控的

synchronized轻量级锁部分,基于自旋锁实现;重量级锁部分,基于挂起等待锁实现

4. 可重入锁 vs 不可重入锁

  • Java中的synchronized是可重入锁,一个线程针对同一把锁连续加锁两次,不会死锁

  • C++中的std::mutex是不可重入锁,一个线程针对同一把锁连续加锁两次,会出现死锁

5. 公平锁 vs 非公平锁

  • 公平锁:严格按照先来后到的顺序来获取锁,哪个线程等待的时间长,哪个线程就拿到锁
  • 非公平锁:若干个线程,各凭本事,随机的获取到锁,和线程等待时间就无关了

synchronized属于非公平锁,多个线程尝试获取到这个锁,此时是按照概率均等的方式进行获取

系统本身线程调度的顺序就是随机的,如果需要实现公平锁,就需要引入额外的队列,按照加锁顺序,把这些获取锁的线程入队列,再一个一个出队列

6. 互斥锁 vs 读写锁

  • 互斥锁:一个线程获取到锁并进行加锁,另一个线程就不能对其加锁了

  • 读写锁:多个线程读同一个变量,不会有线程安全问题

    • 读锁和读锁之间,不会产生互斥
    • 写锁和写锁之间,会产生互斥
    • 读锁和写锁之间,会产生互斥

    突出体现的是读操作和读操作之间是共享的,不会互斥的,有利于降低锁冲突的概率,提高并发能力

日常开发中,有很多场景,属于**”读多,写少“**,大部分操作都是读,偶尔有写的操作

  • 如果使用普通的互斥锁,此时,每次读操作之间都会互斥,影响效率

  • 如果使用读写锁,就能够有效的降低锁冲突的概率,提高效率

Java标准库/操作系统api也提供了读写锁的实现

synchronized实现原理

synchronized既是悲观锁,也是乐观锁,既是轻量级锁,也是重量级锁,轻量级锁是自旋锁实现,重量级锁是挂起等待锁实现,是可重入锁,不是读写锁,是非公平锁

那么synchronized如何”自适应“

1. 锁升级

锁升级的过程:

在这里插入图片描述

偏向锁

  • 首次使用synchronized对对象进行加锁的时候,不是真正的加锁,而只是做一个”标记“,非常轻量非常快,几乎没有开销
  • 如果没有别的线程尝试对这个对象加锁,就可以保持这个状态,一直到解锁,解锁也就是修改一下上述标记,几乎没有开销,前述过程就相当于没有任何加锁操作,速度非常快
  • 但是,如果在偏向锁状态下,有某个线程也尝试来对这个对象加锁,立马把偏向锁升级为轻量级锁,实现真正的加锁

上述的升级过程,针对一个锁对象来说,是不可逆的,只能升级不能降级,一旦升级到重量级锁,不会回退到轻量级锁

2. 锁消除

锁消除是一种编译器优化策略

代码中写了加锁操作,编译器和JVM会对当前的代码做出判定,看这个地方是否真的需要加锁,如果不需要加锁,就会自动把加锁操作给优化掉

最典型的就是:在只有一个线程里,使用synchronized

由于编译器优化,需要保证优化后的逻辑和优化前要等价,这里做的是比较保守的,能够起到的作用有限,与之前谈到的偏向锁互不相干,也不冲突

3. 锁粗化

锁的粒度:加锁的范围内,包含多少代码,代码越多,就认为锁的粒度越粗,反之越细

锁粗化:一种优化策略,有些逻辑中,需要频繁加锁解锁,编译器就会自动的把多次细粒度的锁,合并成一次粗粒度的锁

例如:领导安排了三个工作

  • 分三次给领导打电话会把每个工作
  • 一次电话,汇报三个工作

CAS

CAS:compare and swap(比较和交换),这是一条CPU指令,就可以完成比较和交换这一套操作

可以将CAS的流程想象成一个方法

boolean cas(address, reg1, reg2){
	if(*address == reg1){
		把address内存地址的值和reg2寄存器的值进行交换
		return true;
	}
	
	return false;
}

这里说的交换,实际更多的是用来赋值,一般更关心内存中交换后的数据,而不关心reg2寄存器交换后的数据,可以近似认为上述操作把reg2的值赋值给内存中

  • 由于CPU提供了上述指令,因此操作系统内核,也就能够完成上述操作,就会提供出这样的CAS的api,JVM又对于系统的CAS的api进一步封装了,在Java代码中就可以使用CAS操作了

  • 但是实际上,CAS被封装到了一个unsafe包中,容易出错,不鼓励大家直接使用CAS

Java中也有一些类,对CAS进行了进一步的封装,典型的就是原子类

例如java.util.comcurrent.atomia中的AtomicInteger,相当于针对int进行了封装,可以保证此处的++或–操作,是原子的

Java中不支持运算符重载,无法针对原子类进行++、–;C++和python能够支持运算符重载,可以重新定义±*/等各种运算符的作用

package thread;

import java.util.concurrent.atomic.AtomicInteger;

public class Demo38 {
    private static AtomicInteger count = new AtomicInteger(0);

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            //count++
            count.getAndIncrement();
            //++count
            count.incrementAndGet();
            //count--
            count.getAndDecrement();
            //--count
            count.decrementAndGet();
            //count+=10
            count.getAndAdd(10);

        });


        t1.start();
        t1.join();
        System.out.println("count = " + count);
    }
}

此处我们的代码中,没有用到任何加锁操作,使得代码以更高的效率来执行程序

这一套基于CAS不加锁来实现线程安全代码的方式,也成为无锁编程

  • 这一套操作适用范围没有加锁更广泛,针对一些特殊场景,使用CAS是更高效的,但是有些场景,不太适合使用CAS
  • 一种更加折中的办法,可以基于CAS来封装成自旋锁(自旋锁也是基于CAS来实现的),这样做其实也就失去了“无锁编程”的意义了

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

“捡到一束光,日落时还给太阳”

数据结构初阶题解 1.移除元素2.合并两个有序数组3.移除链表元素4.反转链表5.合并两个有序链表6.链表的中间结点7.环形链表的约瑟夫问题8.分割链表有感:其实我早就死了,死在破碎的三观里;死在飘渺的理想里;死在幻想的感情里&#x…

[AHK]自定义消息实现两个脚本之间通信

自己编写的两个脚本,用自定义消息实现,一个脚本控制另一个脚本,让被控脚本挂起或退出。 从aaa.ahk向bbb.ahk发送一个消息,bbb.ahk捕获消息后再进行处理,比如: 从aaa.ahk中向bbb.ahk发送特定的数字&#xff…

gpt-6有望成为通用工具

OpenAI CEO山姆奥特曼(Sam Altman)在最新的博客访谈中,提到gpt-6有望成为通用工具。 奥特曼还认为,目前的模型不够聪明,“使用GPT-2进行科学研究曾被认为是不切实际的想法。而如今,虽然人们使用GPT-4进行科…

短视频评论ID批量爬虫提取获客软件|DY评论下载采集工具

短视频评论批量抓取软件:智能拓客,精准抓取用户反馈 在当今数字化营销时代,了解用户的需求和反馈对于企业的发展至关重要。而作为流行的短视频平台,短视频评论蕴含了丰富的用户信息和市场洞察。为了帮助企业高效获取这些宝贵数据…

一秒内传输50万对纠缠光子?!纽约市量子网络刷新纪录

量子网络技术行业的领军企业Qunnect宣布,在纽约市的GothamQ网络上,其偏振量子比特的传输性能刷新了纪录。Qunnect利用现有的商用光缆实现了每秒传输50万对高保真度纠缠光子的速率,且该网络的正常运行时间超过了99%。 纽约34公里长的GothamQ量…

LIUNX文件系统

目录 1.磁盘的物理结构 2.CHS定位法 3.磁盘的逻辑存储 4.系统层面 inode.block[15] 创建文件的流程 查找文件的流程 了解文件系统,首先要了解磁盘是如何存储和读取数据的。 1.磁盘的物理结构 可以理解这个盘上有很多的小磁铁,通过旋转盘面和摆动…

C# 整数转罗马数字

罗马数字包含以下七种字符:I,V,X,L,C,D和M。 例如,罗马数字2写做 II ,即为两个并列的 1。12 写做XII,即为XII。27写做 XXVII,即为XXV II 。 通常情况下,罗马数字中小的数字在大的数字…

显示msvcp140.dll丢失要如何解决?这5种方法高效修复msvcp140.dll

msvcp140.dll是Microsoft Visual C Redistributable软件包中的一个文件,主要用于支持使用C编程语言编写的软件的正常运行。如果你的电脑出现缺少msvcp140.dll的错误消息,可能会影响到某些程序的启动和运行。然而,不必过度担心,因为…

【SQL每日一练】分组过滤练习题

文章目录 前言MySQL语法注意: 前言 题目:现在运营想查看每个学校用户的平均发贴和回帖情况,寻找低活跃度学校进行重点运营,请取出平均发贴数低于5的学校或平均回帖数小于20的学校。 drop table if exists user_profile; CREATE …

JavaSE:继承 多态

继承 继承的本质 子类能够使用父类的方法和变量 使用场景:代码复用 在一个类中实现了一个很复杂的方法,给一个新类重新实现这个方法,我们直接继承即可 public class Student {public String sno;public void study() {System.out.printl…

2024妈妈杯数学建模思路A题思路汇总分析 MathorCup建模思路分享

C题:移动通信网络中PCI规划问题 (完整版内容放在文末了) 2024MathorCup A题完整思路完整数据可执行代码后续高质量成品论文 l 难度评分: 3.5/5 l 开放度评分: 3/5 l 适合专业: 通信工程、计算机科学、电子工程 l 主要算法: 图论算法、…

02 - Git 之命令 + 删除 + 版本控制 + 分支 + 标签 + 忽略文件 + 版本号

1 Git相关概念 1.1 以下所谈三个区,文件并不只是简单地在三个区转移,而是以复制副本的方式转移 使用 Git 管理的项目,拥有三个区域,分别是 Working area工作区(亦称为 工作树Working Tree)、stage area …

vscode按ctrl+鼠标左键没反应

vscode按ctrl鼠标左键没反应 问题问题解决 问题 新买的阿里云服务器,在连接vscode后,按ctrl鼠标左键没反应,怎么办? 问题解决 你没有在vscode上安装c的相关插件,安装之后才可以实现按ctrl鼠标左键跳转到函数的定义

书生·浦语大模型第二期实战营(4)笔记

Finetune 为什么要微调 适应下游任务 两种微调范式 上面的是增量训练 下面的是指令微调 数据格式 微调方案 lora: 在基座模型的基础上再套用一个小模型 XTuner 简介 快速上手 LnternLM2 1.8B 多模态LLM

springdoc-openapi使用

springdoc-openapi使用 一、引入pom二、新增配置类OpenApiConfig四、Controller层示例五、配置文件新增内容六、验证 一、引入pom <dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-ui</artifactId><version>1…

IT如何与业务双向奔赴,高效并驱共“盈”企业发展

随着经济和技术的飞速发展&#xff0c;在当前数字化时代中&#xff0c;如何进行有效的数字化转型、运用新技术&#xff0c;特别是让AI技术融入企业的具体业务场景、快速实现应用场景的落地、确保企业不落后于时代发展&#xff0c;是每一位CIO都会面临的一项挑战。 IT部门在企业…

Centos7.9 脚本一键部署nextcloud,配置Nginx代理Https。

目录 一键安装nextcloud 出现错误TypeError Cannot read properties of undefined (reading ‘writeText‘) 生成自签名SSL证书 编辑Nginx配置文件 启动Nginx 一键安装nextcloud 本脚本参考文章&#xff0c;本文较长建议先看完在操作&#xff01;&#xff01;&#xff01;…

基于数据库现有表导出为设计文档

1.查询 SELECTCOLUMN_NAME 字段名,COLUMN_COMMENT 字段描述,COLUMN_TYPE 字段类型,false as 是否为主键 FROMINFORMATION_SCHEMA.COLUMNS wheretable_NAME region -- 表名2.查询结果 3.导出为excel

考研OSchap1计算机系统概述

目录 一、概念 p1 二、功能 p3 1.作为计算机资源的管理者 &#xff08;1&#xff09;文件管理 &#xff08;2&#xff09;存储器管理 &#xff08;3&#xff09;处理机管理 &#xff08;4&#xff09;设备管理 2.作为用户与计算机硬件系统之间的接口 &#xff08;1&…

VMware与新插入的虚拟机 版本不兼容解决方法

1、找到虚拟机目录文件 2、用记事本打开修改版本号&#xff08;找到虚拟机版本号&#xff09; 3、如图所示为版本15的型号 4、修改virtualHW.version "15"&#xff08;引号里面对应上面版本号&#xff09; 5、修改成功