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();//++countcount.incrementAndGet();//count--count.getAndDecrement();//--countcount.decrementAndGet();//count+=10count.getAndAdd(10);});t1.start();t1.join();System.out.println("count = " + count);}
}

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

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

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

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

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

相关文章

自助棋牌室如何用一招留住80%的客户?

棋牌室如何用一招守住80%的回头客,你想知道吗? 记得收藏,希望对你有一点点帮助! 今天我就分享一个非常简单的方法,简单到所有的棋牌室老板你立马就可以去执行的方法!第一步,加好友,…

Java面试八股之Iterator接口和Iterable接口

1. Java为什么不直接实现Iterator接口,而是实现Iterable? 这道题算是一道比较基础的题,面试官肯定也不是想让回答得多深入,只是考查对迭代器的了解程度,最好是看过源码,实际上迭代器的源码并不难。我们把注释折叠起来…

LIUNX文件系统

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

葡韵饼店:云上清明节,千里寄哀思

清明,又称踏青节、祭祖节、行清节,节期在仲春与暮春之交,源自于上古时代的祖先信仰和春祭礼俗,兼具人文与自然两大内涵。 每当到了这个时候,人们都会携带祭祀物品,与家人们齐聚结伴,登山祭祖&am…

支付宝支付之SpringBoot整合支付宝创建自定义支付二维码

文章目录 自定义支付二维码pom.xmlapplication.yml自定义二维码类AlipayService.javaAlipayServiceImpl.javaAlipayController.javaqrCode.html 自定义支付二维码 继&#xff1a;SpringBoot支付入门 pom.xml <dependency><groupId>org.springframework.boot<…

前端开发该不该“跳槽”到鸿蒙?

前言 面对互联网行业的激烈竞争&#xff0c;许多人都深感2023年已是不易&#xff0c;而展望2024年&#xff0c;似乎更是难上加难。这一切的根源&#xff0c;皆因行业多年发展后&#xff0c;人才市场的饱和现象愈发严重。那么&#xff0c;作为前端开发者&#xff0c;我们究竟该…

【尚硅谷】Git与GitLab的企业实战 学习笔记

目录 第1章 Git概述 1. 何为版本控制 2. 为什么需要版本控制 3. 版本控制工具 4. Git简史 5. Git工作机制 6. Git和代码托管中心 第2章 Git安装 第3章 Git常用命令 1. 设置用户签名 1.1 基本语法 1.2 案例实操 2. 初始化本地库 2.1 基本语法 2.2 案例实操 3. 查…

【运输层】TCP 的流量控制和拥塞控制

目录 1、流量控制 2、TCP 的拥塞控制 &#xff08;1&#xff09;拥塞控制的原理 &#xff08;2&#xff09;拥塞控制的具体方法 1、流量控制 一般说来&#xff0c;我们总是希望数据传输得更快一些。但如果发送方把数据发送得过快&#xff0c;接收方就可能来不及接收&#x…

Ubuntu 22.04 配置VirtualBox安装Windows 10虚拟机

Ubuntu 22.04 配置VirtualBox安装Windows 10虚拟机 文章目录 Ubuntu 22.04 配置VirtualBox安装Windows 10虚拟机1.安装virtualbox2.下载Window.iso文件并载入3.问题解决3.1 Kernel driver not installed (rc-1908)3.2 VT-x is disabled in the BIOS for all CPU modes 4.安装Wi…

带你实现一个github注册页面的星空顶

带你实现一个github注册页面的星空顶 github的注册页面可以说是非常的好看&#xff0c;如果没有看过的可以看下面的图片&#xff1a; 那么要如何实现下面的这个效果呢&#xff1f; 首先我们研究一下他的这个官网 首先我看到的后面的这个背景&#xff0c;是不是一个纯色的背景…

Linux安装Docker完整教程及配置阿里云镜像源

官网文档地址 安装方法 1、查看服务器内核版本 Docker要求CentOS系统的内核版本高于3.10 uname -r #通过 uname -r 命令查看你当前的内核版本2、首先卸载已安装的Docker&#xff08;如果有&#xff09; 2.1 确保yum包更新到最新 yum update2.2 清除原有的docker&#xff0c…

02_Fixture定位,Caliper卡尺工具,几何学工具

Fixture定位工具 需求: 测量工件的尺寸 使用Caliper(卡尺)工具 这个时候需要借助Fixture工具 VisionPro中的图像空间 “” 图像的当前空间&#xff0c;即CogImage中的“SelectedSpaceName”表示的名字空间 “#” 像素空间&#xff0c;即坐标原点为图片左上角的坐标空间&am…

TCP/IP协议—MQTT

TCP/IP协议—MQTT MQTT协议MQTT协议特点MQTT通信流程MQTT协议概念 MQTT报文固定报头可变报头有效载荷 MQTT协议 消息队列遥测传输&#xff08;Message Queuing Telemetry Transport&#xff0c;MQTT&#xff09;是一个基于客户端-服务器的消息发布/订阅传输协议。它的设计思想…

windows上安装make

下载地址 https://sourceforge.net/projects/gnuwin32/ 点击框中的下载&#xff0c;下载后安装。把安装路径添加到环境变量 PATH 中. 打开cmd&#xff0c;验证是否生效 安装包下载地址&#xff1a; https://download.csdn.net/download/qq_36314864/89163210

python读取DBF数据

DBF文件通常是由数据库软件&#xff08;如FoxPro或dBASE&#xff09;创建的数据库文件。Python中并没有直接读取DBF文件的内置库&#xff0c;但你可以使用第三方库如dbfread来读取DBF文件。 首先&#xff0c;你需要安装dbfread库。你可以使用pip来安装&#xff1a; pip insta…

【人工智能书籍分享】从ChatGPT到AIGC:人工智能重塑千行百业

今天又来给大家推荐一本人工智能方面的书籍<从ChatGPT到AIGC&#xff1a;人工智能重塑千行百业>。本书介绍了ChatGPT的前世今生&#xff0c;重点聚焦普通人如何使用ChatGPT获得工作和生活效率的提升&#xff0c;各行各业如何通过ChatGPT来改变自己的赛道状态。 使用Chat…

免费SSL证书的不香吗?四步轻松搞定

随着技术的发展和普及&#xff0c;现在有许多机构提供免费的SSL证书&#xff0c;使得即便是拥有有限预算的网站也能够享受到基本的加密服务。几乎所有的公有云服务商都提供了免费版ssl&#xff0c;如阿里云曾推出免费型DV SSL证书&#xff0c;腾讯云和百度云也有类似的免费SSL证…

平衡车设计——硬件篇

在本文开始之前我忍不住想吐槽一个事情&#xff0c;就在前两天晚上&#xff0c;我满意地装完平衡车&#xff0c;给他取了个名字叫瓦力&#xff08;没错&#xff0c;就是机器人总动员里的瓦力&#xff09;&#xff0c;他长这个样子。 把他放到桌子上放了一夜&#xff0c;第二天早…

ffmpeg buffer管理

除了内存管理之外&#xff0c;数据的前后级流转也涉及到buffer管理. 个人觉得ffmpeg里面的buffer管理实现极为巧妙&#xff0c;也很值得借鉴. 概述 重要数据结构 AVBufferPool 在libavutil/buffer_internal.h中定义&#xff0c;为内部数据结构&#xff0c;不能在应用程序中直…

基于Java的XxlCrawler网络信息爬取实战-以中国地震台网为例

目录 前言 一、信息网站介绍 1、网站介绍 2、 地震历史信息 3、 历史信息接口分析 二、XxlCrawler组件 1、关于XxlCrawler 2、核心概念介绍 三、实际信息爬取 1、新建maven项目 2、新建model层对象 3、实际爬取 总结 前言 如今&#xff0c;只要谈起网络信息爬取也就…