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

news2024/12/25 0:52:06

目录

  • 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来实现的),这样做其实也就失去了“无锁编程”的意义了

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

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

相关文章

CANfestival 主机进入预操作态(preOperational)自动发送复位节点指令。

核心是iam_a_slave ,这个是字典生产的时候自动生成的。

xftp、xshell连不上虚拟机解决方法

一、检查连接虚拟机ip看是否正确 查看虚拟机系统 IP ifconfig 二、检查虚拟机防火墙是否关闭 查看防火墙状态(ubuntu) sudo ufw status 关闭防火墙 sudo ufw disable 查看防火墙状态(centos) systemctl status firewalld.service 关闭防火墙 systemctl stop firewalld.se…

vscode按ctrl+鼠标左键没反应

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

FTP客户端Transmit 5 for Mac中文激活版

Transmit 5是一款功能强大的Mac FTP客户端软件,它由Panic公司开发,为用户提供简单、高效的文件传输体验。 Transmit 5 for Mac中文激活版下载 Transmit 5支持多种传输协议,如FTP、SFTP、WebDAV和Amazon S3等,满足用户不同的文件传…

学习笔记-数据结构-线性表(2024-04-17)

设计一个算法实现在单链表中删除值相同的多余节点的算法。 设计思想:双指针 变量说明: head - 参数变量,代表链表的头节点。在调用DelSameNum函数时,需要传递链表的头节点的地址给这个参数,从而允许函数对链表进行操作…

工业控制(ICS)---modbus

Modbus Modbus,市场占有率高、出题频率高,算是最常见的题目,因为这个协议也是工控领域最常见的协议之一,主要有三类 Modbus/RTU 从机地址1B功能码1B数据字段xBCRC值2B 最大长度256B,所以数据字段最大长度252B Modbus/ASCII …

精度优于3cm!用它做边坡应急地形测量,准!快!

为贯彻落实《广东省地质灾害防治“十四五”规划》,广州市开展地质灾害风险点及新增隐患点专业监测设备建设项目。广州市现存166处地质灾害隐患点全部纳入“一张图”管理,除部分正在开展工程治理的隐患点,全市有138处隐患点已部署专业监测设备…

uni-app HBuilderX通过easycom省略import自动导入自定义组件

快速尝试 自HBuilderX 2.5.5起支持easycom组件模式。更新HBuilderX即可尝试。 easycom默认已启用,并对项目下的components和uni_modules目录开启自动扫描,对符合下面路径和命名规则的组件自动导入。 components/组件名/组件名.vue uni_modules/组件名/…

如何使用WinSCP通过固定公网TCP地址实现远程连接内网设备传输文件

文章目录 1. 简介2. 软件下载安装:3. SSH链接服务器4. WinSCP使用公网TCP地址链接本地服务器5. WinSCP使用固定公网TCP地址访问服务器 1. 简介 ​ Winscp是一个支持SSH(Secure SHell)的可视化SCP(Secure Copy)文件传输软件,它的主要功能是在本地与远程计…

C++笔试强训day1

目录 1.数字统计 2.两个数组的交集 3.点击消除 1.数字统计 链接 题目非常简单&#xff0c;不做赘述&#xff1a;就是单纯利用循环逐个%2判断是否0 详细代码&#xff1a; #include <iostream> using namespace std; int main() {ios::sync_with_stdio(0);cin.tie(0);…

vue快速入门(二十九)echarts在vue中的使用

注释很详细&#xff0c;直接上代码 上一篇 新增内容 echarts.js的下载途径echarts的饼图示范 echarts.js&#xff0c;点击跳转&#xff0c;右键另存即可 源码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><m…

「C++」掌握C++异步编程

&#x1f4bb;文章目录 &#x1f4c4;前言异步任务概念期待与承诺futurepromise异常处理 执行异步任务asyncpackaged_task &#x1f4d3;总结 &#x1f4c4;前言 异步任务是多线程编程的核心&#xff0c;若想学习多线程设计&#xff0c;深入了解这些基本概念是必不可少的。如果…

GPT-3.5和GPT-Plus的区别

GPT-3.5和GPT-Plus都是OpenAI开发的大型语言模型,但它们之间有一些区别: GPT-3.5就是大家熟知的ChatGPT GPT-Plus 是Open AI 的更强的AI模型GPT-4版本。两者区别是&#xff1a; 模型规模:GPT-Plus是GPT-3的一个更大版本,参数量更多。而GPT-3.5是GPT-3的一个优化版本,在参数量…

欧科云链:香港虚拟资产OTC合规在即,技术监管成市场规范关键

4月12日香港OTC发牌制度公众咨询结束后&#xff0c;欧科云链研究院在星岛日报发表专栏文章&#xff0c;分享对香港OTC市场的调研情况&#xff0c;并提出“技术监管是香港OTC及Web3生态走向规范的关键”。欧科云链研究院认为&#xff0c;随着OTC监管及虚拟资产现货ETF等事件向前…

ruoyi 代码生成

子模块 ruoyi-generator 导入表信息 导入表信息时系统会默认读取ruoyi-generator/src/main/resources/generator.yml路径下的配置,配置如下 # 代码生成 gen:# 作者author: ruoyi# 默认生成包路径 system 需改成自己的模块名称 如 system monitor toolpackageName: com.ruoy…

Proteus 8.17 2024年最新版 安装和汉化教程

Proteus是一款专业的电路设计和仿真软件&#xff0c;被广泛应用于电子设计、嵌入式系统开发等领域&#xff0c;是世界上唯一将电路仿真软件、PCB设计软件和虚拟模型仿真软件三合一的设计平台。 软件下载&#xff1a;长按识别关注“我爱单片机”公众号&#xff0c;回复“protues…

C语言进阶课程学习记录-第38课 - 动态内存分配

C语言进阶课程学习记录-第38课 - 动态内存分配 内存动态分配实验-malloc(0)实验实验-realloc和calloc小结 本文学习自狄泰软件学院 唐佐林老师的 C语言进阶课程&#xff0c;图片全部来源于课程PPT&#xff0c;仅用于个人学习记录 内存动态分配 实验-malloc(0) #include <st…

PMP报考别跟风!搞懂这些问题不踩坑!

1.PMP是什么&#xff1f; 1.PMP(Project ManagementProfessional)的中文全称是项目管理专业人士资格认证。该认证是由美国项目管理协会PMI在全球206个国家发起的针对项目经理的资格认证。 2.PMP认证是目前国际上项目管理领域认可度和含金量最高的证书。通过PMP就证明你的项目…

3.1 海思SS928开发 - 烧写工具 - ToolPlatform 安装及配置

3.1 烧写工具 - ToolPlatform 安装及配置 ToolPlatform 安装 进入到开发虚拟机&#xff0c;将文件 ~/hiss928/sdk/ema_2.0.2.2/pc/ToolPlatform/ToolPlatform-1.0.11-win32-x86_64.zip 拷贝至 PC 上。PC 要求安装了 win7 及以上的操作系统。解压压缩包 ToolPlatform-1.0.11-w…

ffmpeg入门

ffmpeg入——安装 Fmpeg地址 FFmpeg源码地址&#xff1a;https://github.com/FFmpeg/FFmpeg FFmpeg可执行文件地址&#xff1a;https://ffmpeg.org/download.html Windows平台 Windows平台下载解压后如图所示&#xff08;文件名称以-share结尾的是开发库&#xff09; FFmpeg…