Java面试——锁

公平锁: 是指多个线程按照申请锁的顺序来获取锁,有点先来后到的意思。在并发环境中,每个线程在获取锁时会先查看此锁维护的队列,如果为空,或者当前线程是等待队列的第一个,就占有锁,否则就会加入到等待队列中,以后会按照 FIFO 的规则从队列中取到自己。

非公平锁: 指多个线程获取锁的顺序并不是按照申请锁的顺序,上来就尝试占有锁,如果尝试失败,就再采用类似公平锁的方式获取锁。有可能后申请的线程比先申请的线程优先获取锁,在高并发的情况下,有可能会造成优先级反转或者饥饿现象。

ReentrantLock:并发包中ReentrantLock的创建可以指定构造函数的boolean类型来得到公平锁或非公平锁,默认是false(非公平锁)。非公平的优点在于吞吐量比公平锁大。对于Synchronized锁也是一种非公平锁。

可重入锁(又名递归锁): 指同一线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码。也就是说,线程可以进入任何一个它已经拥有的锁,所同步的代码块。synchronizedunlock都是可重入锁。

//简单理解,就是方法1 是一个同步方法,里面包含了一个方法2 也是同步方法,但是当进入方法1后,也就获得了方法2的锁,即可重入锁
public synchronized void method1(){System.out.println("方法1 synchronized");method2();
}public synchronized  void method2(){System.out.printf("方法2 synchronized");
}

自旋锁: 是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式尝试获取锁,这样的好处是减少了上下文切换的消耗,确定是循环会消耗CPU。循环比较直到成功为止。

public final int getAndAddInt(Object var1, long var2, int var4){int var5;do{//根据对象和地址偏移量获取内存中的值var5 = this.getIntVolatile(var1, var2);//将获取到的值 var5 传入,此方法内部会先比较var2地址的值是否等于 var5,相等则修改var5值并返回,否则重新进入循环。}while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));return var5;
}

手写一个自旋锁: 思想就是通过while中的循环条件来充当锁,当条件成立时,表示未获得锁,进行死循环,直到while条件不成立,也就是获得锁。就退出死循环,执行业务逻辑。具体查看如下代码:

public class Test {public static void main(String[] args) throws Exception {/*执行结果展示:  AA   myLockBB   myLockAA    unLockBB    unLock*  分析:我们 AA 线程休眠了 5秒足以让 BB 线程执行结束,那为什么 BB 执行到 myLock 之后就没有继续执行呢。*  其实,BB 一直执行着,只不过陷入了 while 死循环中,因为 AA 将线程置为非空。*  等到 5 秒后,AA unlock 重新将线程=null时,BB 获取线程并执行任务。over*/Test test = new Test();new Thread(()->{test.myLock();try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}test.unLock();},"AA").start();TimeUnit.SECONDS.sleep(1);new Thread(()->{test.myLock();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}test.unLock();},"BB").start();}//对线程保证原子性AtomicReference<Thread> atomicReference = new AtomicReference<>();//获取锁,其实质,将锁看做一个条件判断,只要这个判断能够保证线程安全即可。//如下:我们将线程是否为空作为条件,如果是空的就没锁,自己可以对其加锁,将其值设为自己。//如果使用完,使用unlock 将线程设置为 null,其他线程通过判断来获得锁,其实就像一种约定而已。public void myLock(){Thread thread = Thread.currentThread();System.out.println(thread.getName()+"   myLock");while (!atomicReference.compareAndSet(null,thread)){}}//释放锁public void unLock(){Thread thread = Thread.currentThread();atomicReference.compareAndSet(thread,null);System.out.println(thread.getName()+"    unLock");}
}

自旋锁的优点主要包括:
【1】减少线程阻塞:对于锁竞争不激烈且锁占用时间短暂的情况,自旋锁能够显著提高性能,因为它减少了线程因阻塞而产生的上下文切换开销。
【2】避免内核态切换:与非自旋锁相比,自旋锁在尝试获取锁失败时会继续执行循环而不立即陷入内核态,这样可以避免线程在用户态和内核态之间的频繁切换,这在一定程度上提高了系统的整体性能。

然而,自旋锁也存在一些缺点:
【1】高负载下效率低下:如果锁竞争激烈或持有锁的线程需要长时间执行同步块,自旋锁会因为不断重复无效的旋转操作而导致性能下降。在这种情况下,自旋锁的消耗可能会超过线程阻塞后的恢复成本,因此应该关闭自旋锁以避免不必要的性能损失。1234
【2】可能存在不公平性:某些自旋锁实现(如Java中的)不是完全公平的,这意味着它们可能无法为等待时间最长线程提供优先权,这可能导致所谓的“线程饥饿”问题。
【3】单核处理器上的限制:在单核处理器上,自旋锁实际上没有真正的并行性,因为即使当前线程不阻塞其他线程,锁仍然不会被释放,导致资源的浪费。此外,如果处理器数量少于线程数量,自旋锁也可能造成不必要的资源浪费。4
【4】不适合计算密集型任务:如果任务主要是计算密集型的,使用自旋锁可能会导致性能下降,因为自旋锁会占用CPU资源,而在计算密集型任务中,减少锁的使用可能是更优的选择。

综上所述,自旋锁适用于锁竞争不太激烈且锁占用时间较短的场景,但在竞争激烈或锁占用时间较长的情况下,其性能优势不明显,甚至可能导致性能下降。

【独占锁】(写锁): 指该锁只能被一个线程所持有。对ReentrantLockSynchronized而言都是独占锁。
【共享锁】(读锁): 指该锁可被多个线程持有。

【1】不加读写锁时,代码及出现的问题如下:创建5个线程进行写入,5个线程进行读取。

public class ReadWriteLock {private volatile Map map = new HashMap();//写入方法public void put(String k,Object v){System.out.println(Thread.currentThread().getName()+"   开始写入:"+k );try {TimeUnit.MICROSECONDS.sleep(30);}catch (Exception e){e.printStackTrace();}map.put(k,v);System.out.println(Thread.currentThread().getName()+"   写入完成");}//读方法public void get(String k){System.out.println(Thread.currentThread().getName()+"   读数据开始:"+k );try {TimeUnit.MICROSECONDS.sleep(10);}catch (Exception e){e.printStackTrace();}Object v = map.get(k);System.out.println(Thread.currentThread().getName()+"   读数据完场完成"+v);}public static void main(String[] args) {ReadWriteLock readWriteLock = new ReadWriteLock();//写入数据for (int i=1;i<6;i++){final int tempInt = i;new Thread(()->{readWriteLock.put(tempInt+"",tempInt+"");},String.valueOf(i)).start();}//读取数据for(int i=1;i<6;i++){final int tempInt = i;new Thread(()->{readWriteLock.get(tempInt+"");},String.valueOf(i)).start();}}
}

【2】上述代码输出如下:第一个线程未写入完成时,其他线程就进入了该方法,进行了写操作。不符合多线程安全特性。

在这里插入图片描述

【3】加入读写锁:ReentrantReadWriteLock(读写锁)位于JUC包下

public class ReadWriteLock{private volatile Map map = new HashMap();private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();//写入方法public void put(String k,Object v){rwLock.writeLock().lock();try {System.out.println(Thread.currentThread().getName()+"   开始写入:"+k );try {TimeUnit.MICROSECONDS.sleep(30);}catch (Exception e){e.printStackTrace();}map.put(k,v);System.out.println(Thread.currentThread().getName()+"   写入完成");}catch (Exception e){e.printStackTrace();}finally {rwLock.writeLock().unlock();}}//读方法public void get(String k){rwLock.readLock().lock();try {System.out.println(Thread.currentThread().getName()+"   读数据开始:"+k );try {TimeUnit.MICROSECONDS.sleep(10);}catch (Exception e){e.printStackTrace();}Object v = map.get(k);System.out.println(Thread.currentThread().getName()+"   读数据完场完成"+v);} catch (Exception e) {e.printStackTrace();} finally {rwLock.readLock().unlock();}}public static void main(String[] args) {ReadWriteLock readWriteLock = new ReadWriteLock();//写入数据for (int i=1;i<6;i++){final int tempInt = i;new Thread(()->{readWriteLock.put(tempInt+"",tempInt+"");},String.valueOf(i)).start();}//读取数据for(int i=1;i<6;i++){final int tempInt = i;new Thread(()->{readWriteLock.get(tempInt+"");},String.valueOf(i)).start();}}
}

【4】加入读写锁后,输出如下:
在这里插入图片描述

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

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

相关文章

VB6添加资源文件总是内存溢出?最终我还是治好了这胎里病!

网管小贾 / sysadm.cc 说来也奇怪&#xff0c;话说不久前我刚刚解决了 VB6 释放资源文件的一个 BUG &#xff0c;心里正美滋滋的。 不料居然还有个巨大的 BUG 在后边等着我呢&#xff01; 真是不说不知道&#xff0c;一说吓一跳&#xff0c;十天找 BUG &#xff0c;N把辛酸泪…

matlab 凸轮轮廓设计

1、内容简介 略 46-可以交流、咨询、答疑 2、内容说明 略 4 取标段的分析 取标装置是贴标机的核心部件之一&#xff0c;是影响贴标质量和贴标精度的重要因素&#xff0c;取标段是通过取标板与标签的相切运动使得涂有胶水的取标板从标签盒中粘取标签纸[4]&#xff0c;理论…

如何下载B站高清视频、音频到本地?

在B站上找到了喜欢的视频&#xff1f;想要将它保存到本地或者与朋友分享&#xff1f;本文将向您详细介绍一种简单而有效的方法&#xff0c;帮助我们轻松下载并导出B站视频&#xff0c;以便随时欣赏或分享。 一、如何下载并导出B站视频&#xff1f; 手机端/平板端 第一步&…

「连载」边缘计算(十九)02-22:边缘部分源码(源码分析篇)

&#xff08;接上篇&#xff09; 从启动函数Start(&#xff09;中可以看到&#xff0c;其以go routine的方式启动很多后台处理服务&#xff0c;具体如下。 1&#xff09;初始化edged的kubeClient&#xff0c;具体如下所示。 // use self defined client to replace fake kube…

基于AI将普通RGB图像转换为苹果Vision Pro支持的空间照片

将 RGB 图像转换为空间图片 一、引言 随着AR和VR技术的普及,空间照片格式(.HEIC)逐渐受到关注。这种格式允许用户在AR/VR设备上体验到更为真实的立体空间效果。为了让更多的普通图片也能享受这种技术,我们开发了这款可以将普通RGB图像转换为苹果Vision Pro支持的.HEIC格式的…

leetcode:47.全排列2

1.与上一题不同的是&#xff0c;本题目的数组中有重复的数字&#xff0c;所以最终结果需要去重&#xff01; 2.树形结构&#xff1a;先将原来的数组进行排序&#xff0c;方便之后的去重&#xff01;同一数层上不能取重复的元素&#xff01; 3.代码实现&#xff1a; 1.当path…

k8s部署 多master节点负载均衡以及集群高可用

一、k8s 添加多master节点实验 1、master02节点初始化操作 2、在master01节点基础上&#xff0c;完成master02节点部署 ①从master01节点复制所需要的文件 需要从master01节点复制etcd数据库所需要的ssl证书、kubernetes安装目录&#xff08;二进制文件、组件与apiserver通信…

CleanMyMac4苹果Mac电脑全面、高效的系统清理工具

CleanMyMac 4 for Mac是一款专为Mac用户设计的系统清理和优化工具。它具备多种功能&#xff0c;旨在帮助用户轻松管理和释放Mac上的磁盘空间&#xff0c;同时提升系统性能。 系统垃圾清理&#xff1a;CleanMyMac 4能够深入扫描Mac的每一个角落&#xff0c;智能识别并清除不需要…

Java中for循环效率比较

先说结论&#xff1a; 对于遍历一个1千万条数据的arrayList&#xff0c;fori循环最快&#xff0c;iterator其次&#xff0c;foreach再其次&#xff0c;forStream最慢。 测试代码 public class ForSpeedTest {// private static final long FOR_COUNT 10000_0000;private s…

wpf 简单实验 数据更新 列表更新

1.概要 1.1 需求 一个列表提供添加修改删除的功能&#xff0c;添加和修改的内容都来自一个输入框 1.2 要点 DisplayMemberPath"Zhi"列表.ItemsSource datalist;(列表.SelectedItem ! null)(列表.SelectedItem as A).Zhi 内容.Text;datalist.Remove((列表.Selec…

Java编程与数据库技术:疫情居家办公的坚实后盾

✍✍计算机毕业编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java、…

canvas坐标系统 webgl坐标系统 uv纹理坐标系统 原点

一、canvas原点在左上角&#xff0c;x轴正方向向右&#xff0c;y轴正方向向下&#xff0c;一个点对应一个像素 二、webgl原点在正中间&#xff0c;x轴正方向向右&#xff0c;y轴正方向向上&#xff0c;数据范围在[-1,1]之间 三、uv原点在左下角&#xff0c;x轴正方向向右&#…

华为OD机试真题-求最多可以派出多少支团队-2023年OD统一考试(C卷)---Python3--开源

题目&#xff1a; 考察内容&#xff1a; if while&#xff1b;break; sort&#xff08;&#xff09;&#xff1b;for 代码&#xff1a; """ analyze: 团队&#xff1a;1-2 1个人只能参加一个团队input: 总人数 int 每个人能力&#xff0c;list 最低能力值 …

离散数学复习笔记

一、数理逻辑 命题逻辑的基本概念 命题&#xff1a;能够判断真假的陈述句&#xff08;命题真值唯一&#xff09; 真值唯一的解释&#xff1a; 原子命题 > 复合命题

matlab simulink变压器温度仿真

1、内容简介 略 48-可以交流、咨询、答疑 2、内容说明 略 3、仿真分析 略 matlab simulink变压器温度仿真_哔哩哔哩_bilibili 4、参考论文 略 大型油浸风冷变压器绕组温度场分析_高原 基于顶层油温的变压器绕组热点温度计算改进模型_陈伟根 基于热电类比理论的油浸式电…

《Docker 简易速速上手小册》第9章 Docker 与持续集成(2024 最新版)

文章目录 9.1 持续集成的基本概念9.1.1 重点基础知识9.1.2 重点案例&#xff1a;Python Web 应用的 CI 流程9.1.3 拓展案例 1&#xff1a;Python 数据分析项目的 CI9.1.4 拓展案例 2&#xff1a;Python 微服务的 CI/CD 9.2 Docker 在 CI/CD 中的应用9.2.1 重点基础知识9.2.2 重…

第三节:Vben Admin登录对接后端login接口

系列文章目录 第一节&#xff1a;Vben Admin介绍和初次运行 第二节&#xff1a;Vben Admin 登录逻辑梳理和对接后端准备 文章目录 系列文章目录前言一、Flask项目介绍二、使用步骤1.User模型创建2.迁移模型3. Token创建4. 编写蓝图5. 注册蓝图 三. 测试登录总结 前言 上一节&…

UE5 C++ Widget练习 Button 和 ProgressBar创建血条

一. 1.C创建一个继承Widget类的子类&#xff0c; 命名为MyUserWidget 2.加上Button 和 UserWidget的头文件 #include "CoreMinimal.h" #include "Components/Button.h" #include "Blueprint/UserWidget.h" #include "MyUserWidget.genera…

kotlin与java的相互转换

Kotlin转java 将kotlin代码反编译成java Tools -> Kotlin -> Show Kotlin Bytecode 然后点击 【Decompile】 生成java代码 java转kotlin Code -> Convert Java File To Kotlin File

pytorch 用F.normalization的逆归一化如何操作

逆归一化的时候再把这个数乘回去就行了 magnitude a.norm(p2, dim1, keepdimTrue) # NEW atorch.nn.functional.normalize(a, p2, dim1) a_or a* magnitude # NEW print(a_or) Outputs: tensor([]1,2,3)