Redian新闻
>
应用层|关于编程语言中的那些「锁」事

应用层|关于编程语言中的那些「锁」事

公众号新闻

并发锁,在编程语言中并不陌生。不同的语言中,锁的使用大同小异,以 Java 语言中的锁最为典型。我们今天就从什么是锁、我们为什么需要锁、锁的分类、有关锁的应用这四个方面,一起来聊聊与锁有关的那些事。

什么是锁

在日常生活中,锁就是一个安全标志。我们通过上锁和解锁,实现安全离家和回家。在编程语言中,锁也是个安全标志。在多线程并发场景下,给某个变量,或某个方法函数加个锁,代表同一时刻只能有一个线程访问该变量或者该方法函数。同时它又是在许多面试场景中经常会被面试官问到的,接下来结合例子来和大家讨论下悲观锁与乐观锁。

为什么需要锁

先讲概念太枯燥,所以我们举一个通用的生活场景作为例子。A 和 B 两个人分别去车站买从上海到北京的同一时间出发的高铁票。A 排队在 1 号售票窗,B 排队在 2 号售票窗,两人同时递出身份证买票,好巧不巧,1 和 2 号窗口的售票员同时说这班车只有一张票,怎么办?拼哪个窗口的售票员手速更快吗?于是 A 和 B 就开始吵架,可后面排队的很多人都在等。如果 A 和 B 继续吵架僵持不下,就会连带着浪费其他人的时间。经过讨论,大家一致认为,即便是要拼手速也是他俩拼。那么问题来了,用什么来拼呢?这个时候,站长出来了。他说,我这里有一把锁,会放在你们中间的位置上,谁抢到就先给谁买票,这样对 A 和 B 都公平。大家一阵欢呼,一把锁解决了抢票的纷争。这个场景放在编程语言中,就是一个典型的多线程并发场景问题。上述的 A 和 B 是两个线程,高铁票则可以看作是一个变量或者一个方法函数。如果不加锁,高铁票就会被 A 和 B 抢来抢去,变得面目全非,甚至还会出现一开始票价为 50 元,结果抢着抢着可能就被人把价格改到了 100 元。所以,给高铁票(变量或方法函数)加一个锁,谁抢到锁,就有权使用该高铁票(变量或方法函数),保证其安全。

锁的分类和应用场景
以 Java 中的锁为例,Java 中有种类丰富的锁,每种锁因其特性的不同,在不同的场景下展现出的效率也是参差不齐的。这就需要我们对锁进行合理的分类,放对位置,才能更好地使用锁。

a.线程是否给同步资源加锁?

是:悲观锁。

否:乐观锁。


b.同步资源加锁失败,线程是否需要阻塞?

是:阻塞。

否:不阻塞(自旋锁、适应性自旋锁)。


c.多个线程竞争同步资源的细节区别是?

无锁:不锁住资源,多个线程中只有一个能修改资源成功,其它线程会重试。

偏向锁:同一个线程执行同步资源时自动获取资源。

轻量级锁:多个线程竞争同步资源时,没有获取资源的线程自旋等待锁释放。

重量级锁:多个线程竞争同步资源时,没有获取资源的线程阻塞等待唤醒。


d.多个线程竞争锁时要不要排队?

排队:公平锁。

先尝试插队,插队失败再排队:非公平锁。


e.一个线程中的多个流程能不能获取同一把锁?

能:可重入锁。

不能:非可重入锁。


f.多个线程能否共享一把锁?

能:共享锁。

不能:排他锁

为了方便扣友们理解,下面将以乐观锁和悲观锁为例,简单说下锁的应用场景。

乐观锁和悲观锁

先说概念。悲观锁,从这个名字我们就能看出,这个锁的态度是比较悲观的,它会认为自己在使用变量或者方法的同时,一定会有其他的线程来跟它抢,所以,他最爱做的事情就是先加锁。等它使用完,别的线程想做啥就做,手中有锁,并发不愁。

而乐观锁呢,和悲观锁截然不同。它的态度很乐观,每次使用前,它都认为不会有别的线程跟它抢,所以每次都不会加锁,只是在更新某个数据时,才会去判断之前有没有别的线程更新这个数据。如果这个数据没有被更新,当前线程会将自己修改的数据成功写入。如果数据已经被其他线程更新,它就会根据不同的实现方式执行不同的操作(例如报错或者自动重试)。

从两者概念的不同,我们就会知道两者所适合应用的场景也是不一样的。悲观锁适合写操作多的场景,先加锁可以保证写操作时数据正确。乐观锁适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅提升。光说概念有些抽象,我们来看下乐观锁和悲观锁的调用方式例子:

//--悲观锁的调用方式--//synchronizedpublic synchronized void testMethod(){    ...具体方法省略...}//ReentrantLockprivate ReentrantLock lock = new ReentrantLock();//需要保证多个线程使用的是同一个锁public void modifyPublicResources(){    lock.lock();    ...具体方法省略...    lock.unlock();}
//--乐观锁的调用方式--private AtomicInteger atomicInteger = new AtomicInteger();//需要保证多个线程使用的是同一个AtomicIntegeratomicInteger.incrementAndGet();//执行自增1
通过上面的调用方式示例,我们可以发现悲观锁基本都是先加锁之后再操作同步资源,而乐观锁就直接去操作同步资源。

那么,为什么乐观锁能够做到事先不加锁也可以正确的实现线程并发呢?这就涉及到乐观锁的主要实现方式“CAS”的技术原理了,感兴趣的扣友们可以留言,可以再开一篇来给大家聊聊。你们在工作中还遇到过哪些锁,有没有遇到过被锁耽误头秃的时候,也欢迎大家在评论区积极留言~


BY / 

本文作者:海洋

编辑&版式:Janson

声明:本文归“力扣”版权所有,如需转载请联系。


点个在看,少个 bug

微信扫码关注该文公众号作者

戳这里提交新闻线索和高质量文章给我们。
相关阅读
时隔 20 年,这个编程语言再次“称王”!那些意欲取代 C++ 的编程语言,成功了吗?中国的新冠疫情如海啸般到来渡十娘|关于胡鑫宇,我说几句题外话洋人剜眼摘心炼药?一则谣言中的大清危机通过编写“猜数字”游戏来学习 Ada 编程语言 | Linux 中国微软公布 .NET最新的编程语言支持策略What was Karl Marx's original plan for communism?中文编程不如英文香?今年诞生的这些国产编程语言表示不服Rust将迎来爆发式增长;更多国产编程语言进入视野 | 编程语言领域解读在 C 语言中使用 getopt 解析命令行短选项 | Linux 中国看视频涨知识|关于防疫的这些奇葩网传,你信了几个?国内最流行的编程语言调优,它排第一!如果编程语言是人......阿根廷,今天你必需哭泣!这款源自以色列的编程游戏,通过“闯关”教孩子写真实的编程语言i-Refill | ChatGPT爆火背后,AI应用层已经到来腾讯发布 2022 研发大数据报告:Go 语言蝉联最热编程语言源自以色列的编程游戏,通过“闯关”教孩子写真实的编程语言老胡阳了也不足以平民愤硬核观察 #870 C 语言已不再仅仅是一种编程语言C++ 夺冠!2022 年度编程语言从C和C++向Rust等内存安全编程语言的转变正在取得进展C++崛起,摘得TIOBE 2022年度编程语言C++ 夺冠!成为 TIOBE 2022 年度编程语言谷歌最好的程序员Jeff Dean:我用过 18 种编程语言Epic CEO:元宇宙已达6亿用户,元宇宙编程语言Verse意义何在?我们应该如何用好 AI?从 ChatGPT 到编程语言、大数据、前端新开源!跨时代AI编程语言NGPTL++赠书|都2023年了,R语言可以这样学?《R语言编程—基于tidyverse》T12校园评价黑客使用哪些编程语言?巴金:那些年,我在谎言中过日子硬核观察 #911 C++ 之父呼吁改变编程语言本身以提升安全性2023需求最高的编程语言:Python、JavaScript和Java
logo
联系我们隐私协议©2024 redian.news
Redian新闻
Redian.news刊载任何文章,不代表同意其说法或描述,仅为提供更多信息,也不构成任何建议。文章信息的合法性及真实性由其作者负责,与Redian.news及其运营公司无关。欢迎投稿,如发现稿件侵权,或作者不愿在本网发表文章,请版权拥有者通知本网处理。