JAVAEE初阶相关内容第十一弹--多线程(进阶)

目录

一、常见的锁策略

1乐观锁VS悲观锁

1.1乐观锁

1.2悲观锁

2.轻量级锁VS重量级锁

2.1轻量级锁

2.2重量级锁

3.自旋锁VS挂起等待锁

3.1自旋锁

3.2挂起等待锁

4.互斥锁VS读写锁

4.1互斥锁

4.2读写锁

5.公平锁VS非公平锁

5.1公平锁

5.2非公平锁

6.可重入锁VS不可重入锁

6.1可重入锁

6.2不可重入锁

7.关于synchronized

二、CAS

1.CAS涉及的下操作:

2.CAS的应用场景

2.1实现原子类

伪代码​编辑

2.2实现自旋锁

伪代码

3.CAS中的ABA问题

三、Synchronized原理

1.锁升级/锁膨胀

1.1无锁

1.2偏向锁

1.3轻量级锁

1.4重量级锁

2.锁消除

3.锁粗化


一、常见的锁策略

接下来进行学习的内容不仅仅局限于java,任何和“锁”相关的话题,都会涉及到。

1乐观锁VS悲观锁

站在锁冲突概率的预测角度

1.1乐观锁

假设数据一般情况下不会产生并发冲突,所以在数据进行提交更新的时候,才会正式对数据是否产生并发冲突进行检测,如果发现并发冲突了,则让返回用户错误信息,让用户决定如何去做。

1.2悲观锁

总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。

2.轻量级锁VS重量级锁

2.1轻量级锁

加锁解锁开销较小,效率更高

2.2重量级锁

开锁解锁开销较大,效率更低

站在加锁操作的开销角度

3.自旋锁VS挂起等待锁

3.1自旋锁

典型的轻量级锁,更大几率获取到锁,加锁速度快

3.2挂起等待锁

典型的重量级锁,“傻等”,被动的等待,省下来CPU去做别的工作,加锁的时间比较长。

4.互斥锁VS读写锁

4.1互斥锁

互斥锁:就是前面用过的像synchronized这样的锁,提供加锁和解锁两个操作。如果一个线程加锁了,另一个线程也尝试加锁,就会阻塞等待。

4.2读写锁

提供三种操作:(1)针对读加锁。(2)针对写加锁。(3)解锁

基于一个事实:多线程针对同一个变量并发读,这个时候是没有线程安全问题的,也不需要加锁控制。(读写锁就是针对这种情况采取的特殊的处理)

读锁和读锁之间没有互斥。写锁和写锁之间存在互斥。写锁和读锁之间存在互斥。(当前代码中,如果只是读操作,加读锁即可,如果有写操作,加写锁。)

5.公平锁VS非公平锁

5.1公平锁

这里的公平定义为:先来后到。B比C先来的,当A释放锁后,B就能先于C获取到锁。

5.2非公平锁

不遵守先来后到,B和C都有可能获取到锁。

OS内部的线程调度就可视为是随机的,如果不做任何额外的限制,锁就是非公平的,如果要想实现公平锁,就需要额外的数据结构,来记录线程们的先后顺序。

公平锁和非公平锁之间没有好坏之分,关键还得看适用场景。

6.可重入锁VS不可重入锁

6.1可重入锁

一个线程针对一把锁,连续加锁多次不会死锁。

6.2不可重入锁

一个线程针对一把锁,连续加锁两次,出现死锁。

7.关于synchronized

(1)synchronized既是一个悲观锁,也是一个乐观锁

synchronized默认是乐观锁,但是如果发现当前锁竞争比较激烈,就会变成悲观锁。

(2)synchronized既是轻量级锁,也是重量级锁。

synchronized默认是轻量级锁,如果发现当前锁竞争比较激烈的话,就会变成重量级锁。

(3)synchronizaed这里的轻量级锁,是基于自旋锁的方式实现的。synchronized这里的重量级锁是基于挂起等待锁的方式实现的。

(4)synchronized不是读写锁。

(5)synchronized是非公平锁。

(6)synchrnized是可重入锁。

上述谈到的六种锁策略可以视为是“锁的形容词”

二、CAS

全称:Compare and swap  比较和交换

1.CAS涉及的下操作:

我们设内存中的原始数据V,旧的预期值A,需要修改的新值B

1.比较A与的V值是否相等(比较)

2.如果比较相等。将B写入V(交换)

3.返回操作是否成功

此处特别的是,上述的CAS的过程并不是通过一段代码实现的,而是通过一条CPU指令完成的。也就是说CAS操作是原子的,就可以在一定程度上回避线程安全问题,所以说我们解决线程安全问题除了加锁之外就又有了一个新的方向。

CAS可以理解为是CPU给咱们提供的一个特殊的指令,通过这个指令,就可在一定程度上处理线程安全问题。

2.CAS的应用场景

2.1实现原子类

JAVA标准库中提供的类

AtomicInteger count = new AtomicInteger(0);

伪代码

使用原子类来解决线程安全问题代码实现:

创建两个线程,t1和t2,在前面的学习中,当两个线程不加锁的时候就会出现bug,所以采用了加锁策略,这里使用原子类来实现不需要加锁也可以达到预期的效果:

public class ThreadD28 {public static void main(String[] args) throws InterruptedException {AtomicInteger count = new AtomicInteger(0);//使用原子类来解决线程安全问题Thread t1 = new Thread(() ->{for (int i = 0; i < 50000; i++) {//因为java不支持运算符重载,所以只能使用普通方法来表示自增自减count.getAndIncrement();}});Thread t2 = new Thread(() ->{for (int i = 0; i < 50000; i++) {count.getAndIncrement();}});t1.start();t2.start();t1.join();t2.join();System.out.println(count.get());}
}

运行结果:

2.2实现自旋锁

伪代码

3.CAS中的ABA问题

CAS在运行中的核心,检查value和oldValue是否一致,如果一致就视为value没有被修改过,所以进行下一步的交换操作是没问题的。但是需要注意的是,这里的一致,可能是改过但是还原回来的。【买手机,可能是新机也有可能是翻新机,被销售商回收了,经过一些翻新的操作,将外壳换掉,重新包装】

下面看一个取钱的例子(概率极低!!)

以上情况发生的概率极低,但是这种问题一旦出现的话就是容易解决的,提前防患于未然是更好的选择。针对当前的问题,采取的方案就是加上一个版本号想象成初始的版本号是1,每次修改的版本号都+1,然后进行CAS的时候,就不是以金额为准了,而是以版本号为基准,此时版本号要是没变就一定没发生改变(版本号只能增长,不能降低)

三、Synchronized原理

两个线程针对同一个对象加锁,就会产生阻塞等待。

synchronized内部还有一些优化机制,存在的目的是了让这个锁更高效更好用。

1.锁升级/锁膨胀

1.1无锁

1.2偏向锁

在进行加锁的时候,首先要进入到偏向锁的状态。偏向锁并不是真正的加锁,而是占个位置,有需要才会进行加锁,没需要就不必加。相当于“懒汉模式”中提到的懒加载一样。偏向锁的状态,做个标记(这个过程是非常轻量的)如果使用锁的过程中,没有出现锁竞争在synchronized执行完之后,解除偏向锁即可,但是如果使用过程中,另一个线程也尝试加锁,这个时候就会迅速的将偏向锁升级称为真正的加锁状态,另外的一个线程也只能阻塞等待了。

1.3轻量级锁

当synchronized发生锁竞争的时候,就会从偏向锁升级为轻量级锁,此时,synchronized相当于通过自旋的方式来进行加锁的(就类似于上述的CAS中的伪代码)

1.4重量级锁

如果要是很快别人就释放了锁,自旋还是划算的,但是如果迟迟拿不到锁,一直自旋是不划算的,synchronized自旋不是无休止的,自旋到一定程度,就会在再次升级成为重量级锁(挂起等待锁)。这个锁则是基于操作系统的原生API来进行加锁的,linux原生提供了mutex一组API,操作系统内核提供的加锁功能,这个锁会影响到线程的调度。此时如果线程进行了重量级的加锁,并且发生了锁竞争,此时线程会被放在阻塞队列中,不参与CPU的调度。然后直到锁被释放,这个线程才有机会被调度到,并且有机会获取到锁。

2.锁消除

编译器智能的判定,看当前代码是否真的要加锁,如果这个场景不需要加锁,程序员加了,就会自动的把锁消除。

例如StringBuffer,关键的方法有synchronized,但是如果在单线程中使用StringBuffer,synchronized加了也白加,此时编译器就会直接将加锁操作消除。

3.锁粗化

锁的粒度:synchronized包含的代码越多,粒度就越粗,包含的代码越少,粒度就越细。

一般情况下,认为锁的粒度细一点是比较好的,加锁部分的代码是不能并发执行的,锁的粒度越细,能并发的代码就越多,反之则越少。但是有些情况下,锁的粒度粗一些就更好。

十一弹的续集会进行更新这一部分中在面试中的高频考点~

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

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

相关文章

MemJam: A false Dependency attack against constant-time crypto implementations

作者&#xff1a;A. Moghimi, J. Wichelmann, T. Eisenbarth, and B. Sunar. 发布&#xff1a;International Journal of Parallel Programming 时间&#xff1a;Aug 2019. 笔记&#xff1a; 缓存定时攻击 1、攻击原理 共享缓存存在定时侧信道的风险&#xff08;例如在处理…

设计模式课件

设计模式 创建型设计模式的分类&#xff0c;定义结构型设计模式的分类&#xff0c;定义行为型设计模式的分类&#xff0c;定义 设计模式的分类&#xff0c;在23种设计模式中&#xff0c;每一种属于哪一种的设计模式设计模式的应用场景设计模式的图形&#xff08;考察较少&#…

华为云云耀云服务器L实例评测|带宽,磁盘,CPU,内存以及控制台监控测试

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;CSDN领军人物&#xff0c;全栈领域优质创作者✌&#xff0c;CSDN博客专家&#xff0c;阿里云社区专家博主&#xff0c;2023年6月CSDN上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;AWS/阿里云资深使用…

Python从零到一构建项目

随着互联网的发展&#xff0c;网络上的信息量急剧增长&#xff0c;而获取、整理和分析这些信息对于很多人来说是一项艰巨的任务。而Python作为一种功能强大的编程语言&#xff0c;它的爬虫能力使得我们能够自动化地从网页中获取数据&#xff0c;大大提高了效率。本文将分享如何…

【技术分享】RK Android11系统SD卡启动方法

本文基于Purple Pi OH 3566主板&#xff0c;介绍Android11源码的修改&#xff0c;获得可从SD卡启动的Android11系统镜像。 Purple Pi OH作为一款兼容树莓派的开源主板&#xff0c;采用瑞芯微RK3566 (Cortex-A55) 四核64位超强CPU,主频最高达1.8 GHz,算力高达1Tops&#xff0c;…

海外商城小程序如何开发

随着全球化的发展和人们对跨境购物的需求逐渐增加&#xff0c;海外商城小程序成为了众多电商平台的重要组成部分。本文将深入探讨如何搭建海外商城小程序&#xff0c;从技术实现到用户体验设计&#xff0c;为开发者提供专业且有深度的思考&#xff0c;以帮助他们打造出色的跨境…

手写RPC框架--13.优雅停机

优雅停机 优雅停机a.优雅停机概述b.服务端实现优雅停机c.客户端实现优雅停机d.优雅启动 优雅停机 a.优雅停机概述 当我们快速关闭服务提供方时&#xff0c;注册中心感知、以及通过watcher机制通知调用方一定不能做到实时&#xff0c;一定会有延时&#xff0c;同时我们的心跳检…

如何把视频格式转换成mp4?支持的格式种类非常多。

如何把视频格式转换成mp4&#xff1f;随着计算机技术的迅猛发展&#xff0c;我们现在有着各种各样的视频格式可供选择&#xff0c;平时我们都知道的mp4、flv、mov、mkv、avi、wmv等&#xff0c;都是视频格式的种类。其中&#xff0c;MP4是一种具有极佳兼容性的视频格式&#xf…

TikTok魔法:揭秘那个“神奇”的算法

嘿&#xff0c;你是不是每次打开TikTok&#xff0c;都感觉这个应用好像了解你的内心世界一样&#xff1f;没错&#xff0c;背后有一个不为人知、神奇的算法正在起作用&#xff0c;让你欲罢不能。在这篇文章中&#xff0c;我们将揭开TikTok算法的神秘面纱&#xff0c;看看它是如…

车机多用户系统的适配问题

多用户问题出现背景 记录一下多用户的适配问题&#xff1a; 背景是system/app下面新push了两个apk&#xff0c;一个是我们的业务场景apk一个是虚拟车CarService服务的apk&#xff0c;我们的apk需要链接CarService服务通过AIDL通信。 下面这两张图是未roo的情况&#xff08;当…

Python之Xlwings操作excel

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、xlwings简介二、安装与使用1.安装2.使用3.xlwings结构说明 二、xlwings对App常见的操作App基础操作工作簿的基础操作工作表的基础操作工作表其他操作 读取单元格…

MOV导出序列帧并在Unity中播放

MOV导出序列帧并在Unity中播放 前言项目将MOV变成序列帧使用TexturePacker打成一个图集将Json格式精灵表转换为tpsheet格式精灵表导入Unity并播放总结 鸣谢 前言 收集到一批还不错的MG动画&#xff0c;想要在Unity中当特效播放出来&#xff0c;那首先就得把MOV变成序列帧&…

堆排序与TopK问题

一、堆排序 堆排序(升序)&#xff1a;堆排序的思想就是先用数组模拟建大堆&#xff0c;然后把根结点与最后一个结点值交换&#xff0c;最后一个结点的值就是最大值&#xff0c;然后再把前(n-1)个元素重新建大堆&#xff0c;然后根结点与最后一个结点值交换&#xff0c;就找出了…

小红书笔记爬虫

⭐️⭐️⭐️⭐️⭐️欢迎来到我的博客⭐️⭐️⭐️⭐️⭐️ &#x1f434;作者&#xff1a;秋无之地 &#x1f434;简介&#xff1a;CSDN爬虫、后端、大数据领域创作者。目前从事python爬虫、后端和大数据等相关工作&#xff0c;主要擅长领域有&#xff1a;爬虫、后端、大数据…

LNMP架构搭建论坛

目录 一、LNMP简介&#xff1a; 二、LNMP搭建&#xff1a; 1.前提准备&#xff1a; 关闭防火墙和安全机制&#xff1a; 2.编译安装nginx&#xff1a; 3.编译安装mysql&#xff1a; 3.1 安装依赖环境&#xff1a; 3.2 创建mysql运行用户&#xff1a; 3.3 编译安装&#xff1a…

c语言练习题52:写一个函数判断当前机器是大端还是小端

代码&#xff1a; #include<stdio.h> int check_sys() {int a 1;return *(char*)&a;//小端retrun 1 大端return 0&#xff1b; } int main() {if (check_sys() 1) {printf("小端\n");}elseprintf("大端\n"); } 这里首先取a的地址&#xff0c…

原型链(一定要搞懂啊!!!>-<)

一、概念 1、prototype 习惯称作“显示原型”&#xff0c;只有构造函数才有的属性。 2、构造函数 能用new关键字创建的对象叫做构造函数 3、__proto__ 习惯称作“隐式原型”&#xff0c;每一个实例都有的属性&#xff0c;该属性指向他构造函数的“显示原型”。Function对象…

2.14 PE结构:地址之间的转换

在可执行文件PE文件结构中&#xff0c;通常我们需要用到地址转换相关知识&#xff0c;PE文件针对地址的规范有三种&#xff0c;其中就包括了VA&#xff0c;RVA&#xff0c;FOA三种&#xff0c;这三种该地址之间的灵活转换也是非常有用的&#xff0c;本节将介绍这些地址范围如何…

Mac端交互式原型设计 Axure RP 8 for Mac汉化

Axure RP 8是一款专业的交互原型设计工具&#xff0c;它被广泛应用于用户体验设计、界面设计和产品原型制作等领域。该软件提供了丰富的功能和工具&#xff0c;使用户能够创建出具有高度交互性和可视化效果的原型。 Axure RP 8的主要特点和功能包括&#xff1a; 1. 快速原型&a…

产教融合 | 力软联合重庆科技学院开展低代码应用开发培训

近日&#xff0c;力软与重庆科技学院联合推出了为期两周的低代码应用开发培训课程&#xff0c;来自重庆科技学院相关专业的近百名师生参加了此次培训。 融合研学与实践&#xff0c;方能成为当代数字英才。本次培训全程采用线下模式&#xff0c;以“力软低代码平台”为软件开发…