Java面试——锁

news/发布时间2024/5/15 5:39:53

公平锁: 是指多个线程按照申请锁的顺序来获取锁,有点先来后到的意思。在并发环境中,每个线程在获取锁时会先查看此锁维护的队列,如果为空,或者当前线程是等待队列的第一个,就占有锁,否则就会加入到等待队列中,以后会按照 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://www.bcls.cn/QBFR/5052.shtml

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

相关文章

python入门

python入门 一、python基础二、标识符、关键字、变量、代码注释、模块三、数据类型的数据四、运算符五、程序流程控制六、容器类型数据1、序列2、列表3、元组4、集合5、字典 七、字符串1、表示字符串2、字符串与数字类型的转换3、字符串格式化4、操作字符串 一、python基础 py…

【力扣白嫖日记】178.分数排名

前言 练习sql语句&#xff0c;所有题目来自于力扣&#xff08;https://leetcode.cn/problemset/database/&#xff09;的免费数据库练习题。 今日题目&#xff1a; 178.分数排名 表&#xff1a;Scores 列名类型idintscoredecimal 在 SQL 中&#xff0c;id 是该表的主键。 …

国家治理的数据赋能及其秩序生产(二)

国家治理的数据赋能及其秩序生产(二) 文章目录 国家治理的数据赋能及其秩序生产(二)前言三、大数据赋能国家治理的权力嬗变(一) 数据平权(1)大数据带来的平等(2)大数据技术带来的信息传播(二) 数据制衡(1)大数据思维驱动了公共权力的扩散(三) 数据监察(1)大数…

数据安全之路:深入了解MySQL的行锁与表锁机制

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 数据安全之路&#xff1a;深入了解MySQL的行锁与表锁机制 前言基础innodb中锁与索引的关系如何避免表锁 前言 在当今数据密集的应用中&#xff0c;数据库锁成为了确保数据一致性和并发操作的关键工具…

React18源码: reconcliler启动过程

Reconcliler启动过程 Reconcliler启动过程实际就是React的启动过程位于react-dom包&#xff0c;衔接reconciler运作流程中的输入步骤.在调用入口函数之前&#xff0c;reactElement(<App/>) 和 DOM对象 div#root 之间没有关联&#xff0c;用图片表示如下&#xff1a; 在启…

matlab 凸轮轮廓设计

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

蓝桥杯算法 一.

分析&#xff1a; 本题记录&#xff1a;m个数&#xff0c;异或运算和为0&#xff0c;则相加为偶数&#xff0c;后手获胜。 分析&#xff1a; 369*99<36500&#xff0c;369*100>36500。 注意&#xff1a;前缀和和后缀和问题

C#学习总结

1、访问权限 方法默认访问修饰符&#xff1a;private 类默认访问修饰符&#xff1a;internal 类的成员默认访问修饰符&#xff1a;private 2、UserControl的使用 首先添加用户控件 使用时一种是通过代码添加&#xff0c;一种是通过拖动组件到xaml中

Stable Diffusion 绘画入门教程(webui)-ControlNet(线稿约束)

上篇文章介绍了openpose&#xff0c;本篇文章介绍下线稿约束&#xff0c;关于线稿约束有好几个处理器都属于此类型&#xff0c;但是有一些区别。 包含&#xff1a; 1、Canny(硬边缘&#xff09;&#xff1a;识别线条比较多比较细&#xff0c;一般用于更大程度得还原照片 2、ML…

leetcode:46.全排列

1.什么是排列&#xff1f; 有顺序&#xff01;&#xff01; 2.树形结构&#xff1a; 使用used数组进行标记取过的元素&#xff0c;一个元素一个元素地进行取值&#xff0c;取完之后将used数组进行标记。 3.代码实现&#xff1a;&#xff08;循环从i0开始&#xff0c;而不是…

面试经典150题 -- 二叉树搜索树 (总结)

总的链接 : https://leetcode.cn/studyplan/top-interview-150/ 二叉搜索树相关概念 : 二叉搜索树是一个有序树。 若它的左子树不空&#xff0c;则左子树上所有结点的值均小于它的根结点的值&#xff1b;若它的右子树不空&#xff0c;则右子树上所有结点的值均大于它的根结…

element ui 虚拟滚动 滚动到底部加载下一页

引入element ui的InfiniteScroll 无限滚动插件 <div class"news-left"><div class"infinite-list-wrapper" style"overflow:auto;height: 70vh"><ul class"list" v-infinite-scroll"load"><li :class&…

【Python笔记-设计模式】原型模式

一、说明 原型模式是一种创建型设计模式&#xff0c; 用于创建重复的对象&#xff0c;同时又能保证性能。 使一个原型实例指定了要创建的对象的种类&#xff0c;并且通过拷贝这个原型来创建新的对象。 (一) 解决问题 主要解决了对象的创建与复制过程中的性能问题。主要针对…

06 flink 的各个角色的交互

前言 这里主要是 涉及到 flink 中各个角色的交互 TaskManager 和 ResourceManager 的交互 JobMaster 和 ResourceManager 的交互 等等流程 TaskManager 和 ResourceManager 的交互 主要是 包含了几个部分, 如下, 几个菜单 TaskManager向 ResourceManager 注册 Resou…

每日五道java面试题之spring篇(五)

目录&#xff1a; 第一题. 使用 Spring 有哪些方式&#xff1f;第二题. 什么是Spring IOC 容器&#xff1f;第三题. 控制反转(IoC)有什么作用?第四题. IOC的优点是什么&#xff1f;第五题. BeanFactory 和 ApplicationContext有什么区别&#xff1f; 第一题. 使用 Spring 有哪…

辽宁博学优晨教育视频剪辑培训探索学习新意义

在当今数字化快速发展的时代&#xff0c;视频已成为信息传达的重要载体。辽宁博学优晨教育视频剪辑培训应运而生&#xff0c;不仅为学员提供了专业的学习平台&#xff0c;更在探索学习的意义方面赋予了新的内涵。 视频剪辑作为现代媒体行业的重要技能&#xff0c;其学习意义不仅…

信息抽取(UIE):使用自然语言处理技术提升证券投资决策效率

一、引言 在当今快速变化的证券市场中&#xff0c;信息的价值不言而喻。作为一名资深项目经理&#xff0c;我曾领导一个关键项目&#xff0c;旨在通过先进的信息抽取技术&#xff0c;从海量的文本数据中提取关键事件&#xff0c;如企业并购、新产品发布以及政策环境的变动。这些…

【k8s资源调度-HPA(自动扩缩容)】

1、HPA可以做什么&#xff1f; 通过观察pod的cpu、内存使用率或自定义metrics指标进行自动的扩容或缩容pod的数量。通常用于Deployment&#xff0c;不适用于无法扩/缩容的对象&#xff0c;如DaemonSet。控制管理器每隔30s(可以通过-horizontal-pod-autoscaler–sync-period修改…

创建spring项目报错:read time out

在新电脑使用idea创建spring项目时&#xff0c;提示read time out多次尝试无果。 发现只要取消这个选择就可以正常下载。&#xff08;版本是202202&#xff09; 取消勾选后可以正常下载 &#xff08;下载完成后&#xff0c;再次创建sprin项目勾上无影响。&#xff09;

网页403错误(Spring Security报异常 Encoded password does not look like BCrypt)

这个错误通常表现为"403 Forbidden"或"HTTP Status 403"&#xff0c;它指的是访问资源被服务器理解但拒绝授权。换句话说&#xff0c;服务器可以理解你请求看到的页面&#xff0c;但它拒绝给你权限。 也就是说很可能测试给定的参数有问题&#xff0c;后端…
推荐文章