java中怎么判断一段代码时线程安全还是非线程安全_24张图带你彻底理解Java中的21种锁...

(给ImportNew加星标,提高Java技能)

转自:悟空聊架

本篇主要内容如下:

256c384ccdd7d4a0bffd4d56fb9c88e0.png

本篇文章已收纳到我的 Java 在线文档、 Github。我的 SpringCloud 实战项目持续更新中。

帮你总结好的锁:

序号锁名称应用
1乐观锁CAS
2悲观锁synchronized、vector、hashtable
3自旋锁CAS
4可重入锁synchronized、Reentrantlock、Lock
5读写锁ReentrantReadWriteLock,CopyOnWriteArrayList、CopyOnWriteArraySet
6公平锁Reentrantlock(true)
7非公平锁synchronized、reentrantlock(false)
8共享锁ReentrantReadWriteLock中读锁
9独占锁synchronized、vector、hashtable、ReentrantReadWriteLock中写锁
10重量级锁synchronized
11轻量级锁锁优化技术
12偏向锁锁优化技术
13分段锁concurrentHashMap
14互斥锁synchronized
15同步锁synchronized
16死锁相互请求对方的资源
17锁粗化锁优化技术
18锁消除锁优化技术

1. 乐观锁

defb67692ad09bc9e4317fc84992fee2.png

乐观锁是一种乐观思想。假定当前环境是读多写少,遇到并发写的概率比较低,读数据时认为别的线程不会正在进行修改(所以没有上锁)。写数据时,判断当前 与期望值是否相同,如果相同则进行更新(更新期间加锁,保证是原子性的)。

Java 中的乐观锁:CAS。比较并替换,比较当前值(主内存中的值),与预期值(当前线程中的值,主内存中值的一份拷贝)是否一样,一样则更新,否则继续进行 CAS 操作。

如上图所示,可以同时进行读操作,读的时候其他线程不能进行写操作。

2. 悲观锁

cbabb6567bca4cbd70debc18c82ad251.png

悲观锁是一种悲观思想。认为写多读少,遇到并发写的可能性高,每次去拿数据的时候都认为其他线程会修改,所以每次读写数据都会认为其他线程会修改,所以每次读写数据时都会上锁。其他线程想要读写这个数据时,会被这个线程 block,直到这个线程释放锁然后其他线程获取到锁。

Java 中的悲观锁:synchronized 修饰的方法和方法块、ReentrantLock

如上图所示,只能有一个线程进行读操作或者写操作,其他线程的读写操作均不能进行。

3. 自旋锁

da6ada6377a6b541d9fc1ee67e345be2.png

自旋锁是一种技术为了让线程等待,我们只须让线程执行一个忙循环(自旋)

现在绝大多数的个人电脑和服务器都是多路(核)处理器系统,如果物理机器有一个以上的处理器或者处理器核心,能让两个或以上的线程同时并行执行,就可以让后面请求锁的那个线程“稍等一会”,但不放弃处理器的执行时间,看看持有锁的线程是否很快就会释放锁。

自旋锁的优点:避免了线程切换的开销。挂起线程和恢复线程的操作都需要转入内核态中完成,这些操作给 Java 虚拟机的并发性能带来了很大的压力。

自旋锁的缺点:占用处理器的时间,如果占用的时间很长,会白白消耗处理器资源,而不会做任何有价值的工作,带来性能的浪费。因此自旋等待的时间必须有一定的限度,如果自旋超过了限定的次数仍然没有成功获得锁,就应当使用传统的方式去挂起线程。

自旋次数默认值:10次。可以使用参数 -XX:PreBlockSpin 来自行更改。

自适应自旋:自适应意味着自旋的时间不再是固定的,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定的。有了自适应自旋,随着程序运行时间的增长及性能监控信息的不断完善,虚拟机对程序锁的状态预测就会越来越精准。

Java 中的自旋锁:CAS。操作中的比较操作失败后的自旋等待。

4. 可重入锁(递归锁)

8f66667f2848a0a20543066491c73d59.png

可重入锁是一种技术:任意线程在获取到锁之后能够再次获取该锁而不会被锁所阻塞

可重入锁的原理:通过组合自定义同步器来实现锁的获取与释放。

  • 再次获取锁:识别获取锁的线程是否为当前占据锁的线程,如果是,则再次成功获取。获取锁后,进行计数自增,
  • 释放锁:释放锁时,进行计数自减。

Java 中的可重入锁:ReentrantLocksynchronized 修饰的方法或代码段。

可重入锁的作用:避免死锁。

面试题1:可重入锁如果加了两把,但是只释放了一把会出现什么问题?

答:程序卡死,线程不能出来,也就是说我们申请了几把锁,就需要释放几把锁。

面试题2:如果只加了一把锁,释放两次会出现什么问题?

答:会报错,抛出 java.lang.IllegalMonitorStateException。

5. 读写锁

读写锁是一种技术通过 ReentrantReadWriteLock 类来实现。为了提高性能, Java 提供了读写锁。在读的地方使用读锁,在写的地方使用写锁,灵活控制。如果没有写锁的情况下,读是无阻塞的。在一定程度上提高了程序的执行效率。读写锁分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由 JVM 自己控制的。

读锁:允许多个线程获取读锁,同时访问同一个资源。

9a63bb874ac4b1c70cf43363e839d2a4.png

写锁:只允许一个线程获取写锁,不允许同时访问同一个资源。

79a98829c96ad4637186eb85428c8e7d.png

如何使用:

/*** 创建一个读写锁* 它是一个读写融为一体的锁,在使用的时候,需要转换*/private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();

获取读锁和释放读锁:

// 获取读锁rwLock.readLock().lock();// 释放读锁rwLock.readLock().unlock();

获取写锁和释放写锁:

// 创建一个写锁rwLock.writeLock().lock();// 写锁 释放rwLock.writeLock().unlock();

Java中的读写锁:ReentrantReadWriteLock。

6. 公平锁

419c580c8f27134cefcdb0ea585d20b2.png

公平锁是一种思想多个线程按照申请锁的顺序来获取锁。在并发环境中,每个线程会先查看此锁维护的等待队列,如果当前等待队列为空,则占有锁,如果等待队列不为空,则加入到等待队列的末尾,按照 FIFO 的原则从队列中拿到线程,然后占有锁。

7. 非公平锁

356eb7a3d1a9fd75517a535e62142e84.png

非公平锁是一种思想线程尝试获取锁,如果获取不到,则再采用公平锁的方式。多个线程获取锁的顺序,不是按照先到先得的顺序,有可能后申请锁的线程比先申请的线程优先获取锁。

  • 优点:非公平锁的性能高于公平锁。

  • 缺点:有可能造成线程饥饿(某个线程很长一段时间获取不到锁)。

Java 中的非公平锁:synchronized 是非公平锁。ReentrantLock 通过构造函数指定该锁是公平的还是非公平的,默认是非公平的。

8. 共享锁

2a958dc56e7c9b05e022da4302c6819e.png

共享锁是一种思想:可以有多个线程获取读锁,以共享的方式持有锁。和乐观锁、读写锁同义。

Java 中用到的共享锁:ReentrantReadWriteLock

9. 独占锁

b5c1102775bf5eadee31315f1e14c821.png

独占锁是一种思想:只能有一个线程获取锁,以独占的方式持有锁。和悲观锁、互斥锁同义。

Java 中用到的独占锁:synchronizedReentrantLock

10. 重量级锁

62d0f9f3b1ea9e1f88330cfdc6c028fb.png

重量级锁是一种称谓:synchronized 是通过对象内部的一个叫做监视器锁(monitor)来实现的。监视器锁本身依赖底层的操作系统的 Mutex Lock 来实现。操作系统实现线程的切换需要从用户态切换到核心态,成本非常高。这种依赖于操作系统 Mutex Lock来实现的锁称为重量级锁。为了优化 synchonized 引入了轻量级锁,偏向锁。

Java 中的重量级锁:synchronized

11. 轻量级锁

17f84912babe69d2781288871fe7975e.png

轻量级锁是 JDK6 时加入的一种锁优化机制:轻量级锁是在无竞争的情况下使用 CAS 操作去消除同步使用的互斥量。轻量级是相对于使用操作系统互斥量来实现的重量级锁而言的。轻量级锁在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。如果出现两条以上的线程争用同一个锁的情况,那轻量级锁将不会有效,必须膨胀为重量级锁。

  • 优点:如果没有竞争,通过 CAS 操作成功避免了使用互斥量的开销。

  • 缺点:如果存在竞争,除了互斥量本身的开销外,还额外产生了 CAS 操作的开销,因此在有竞争的情况下,轻量级锁比传统的重量级锁更慢。

12. 偏向锁

54c5f3571a1f3dd4d0b8fd47751ad394.png

偏向锁是 JDK6 时加入的一种锁优化机制:在无竞争的情况下把整个同步都消除掉,连 CAS 操作都不去做了。偏是指偏心,它的意思是这个锁会偏向于第一个获得它的线程,如果在接下来的执行过程中,该锁一直没有被其他的线程获取,则持有偏向锁的线程将永远不需要再进行同步。持有偏向锁的线程以后每次进入这个锁相关的同步块时,虚拟机都可以不再进行任何同步操作(例如加锁、解锁及对 Mark Word 的更新操作等)。

  • 优点:把整个同步都消除掉,连 CAS 操作都不去做了,优于轻量级锁。

  • 缺点:如果程序中大多数的锁都总是被多个不同的线程访问,那偏向锁就是多余的。

13. 分段锁

57f7b779d830061a8d1502f57c470150.png

分段锁是一种机制:最好的例子来说明分段锁是 ConcurrentHashMap。

ConcurrentHashMap 原理:它内部细分了若干个小的 HashMap,称之为段(Segment)。默认情况下一个 ConcurrentHashMap 被进一步细分为 16 个段,既就是锁的并发度。如果需要在 ConcurrentHashMap 添加一项 key-value,并不是将整个 HashMap 加锁,而是首先根据 hashcode 得到该 key-value 应该存放在哪个段中,然后对该段加锁,并完成 put 操作。在多线程环境中,如果多个线程同时进行 put 操作,只要被加入的 key-value 不存放在同一个段中,则线程间可以做到真正的并行。

线程安全:ConcurrentHashMap 是一个 Segment 数组, Segment 通过继承ReentrantLock 来进行加锁,所以每次需要加锁的操作锁住的是一个 segment,这样只要保证每个 Segment 是线程安全的,也就实现了全局的线程安全。

14. 互斥锁

73ccab345cf6f283ff9eb9404cf75131.png

互斥锁与悲观锁、独占锁同义,表示某个资源只能被一个线程访问,其他线程不能访问。

  • 读读互斥;
  • 读写互斥;
  • 写读互斥;
  • 写写互斥。

Java 中的同步锁:synchronized

15. 同步锁

3a257228e752d1b9dc6d803b2b9987ac.png

同步锁与互斥锁同义,表示并发执行的多个线程,在同一时间内只允许一个线程访问共享数据。

Java中的同步锁:synchronized

16. 死锁

f3cb501295b419c6b2cc335a495f507a.png

死锁是一种现象:如线程 A 持有资源 x,线程 B 持有资源 y,线程 A 等待线程 B 释放资源 y,线程 B 等待线程 A 释放资源 x,两个线程都不释放自己持有的资源,则两个线程都获取不到对方的资源,就会造成死锁。

Java 中的死锁不能自行打破。所以线程死锁后,线程不能进行响应。所以一定要注意程序的并发场景,避免造成死锁。

17. 锁粗化

2eb48cdd9cb75cf496274aa8b7bd9055.png

锁粗化是一种优化技术:如果一系列的连续操作都对同一个对象反复加锁和解锁,甚至加锁操作都是出现在循环体体之中,就算真的没有线程竞争,频繁地进行互斥同步操作将会导致不必要的性能损耗。所以就采取了一种方案:把加锁的范围扩展(粗化)到整个操作序列的外部,这样加锁解锁的频率就会大大降低,从而减少了性能损耗。

18. 锁消除

b569b69ed859ae722e32008220366c46.png

锁消除是一种优化技术就是把锁干掉。当 Java  虚拟机运行时发现有些共享数据不会被线程竞争时就可以进行锁消除。

那如何判断共享数据不会被线程竞争?

利用逃逸分析技术:分析对象的作用域,如果对象在 A 方法中定义后,被作为参数传递到B方法中,则称为方法逃逸;如果被其他线程访问,则称为线程逃逸。

在堆上的某个数据不会逃逸出去被其他线程访问到,就可以把它当作栈上数据对待,认为它是线程私有的,同步加锁就不需要了。

19. synchronized

09ae15607e76000e1b5df874a87020b2.png

synchronized 是 Java中的关键字:用来修饰方法、对象实例。属于独占锁、悲观锁、可重入锁、非公平锁。

  • 作用于实例方法时,锁住的是对象的实例(this);

  • 当作用于静态方法时,锁住的是类(class)。相当于类的一个全局锁, 会锁所有调用该方法的线程;

  • synchronized 作用于一个非 null 的对象实例时,锁住的是所有以该对象为锁的代码块。它有多个队列,当多个线程一起访问某个对象监视器的时候,对象监视器会将这些线程存储在不同的容器中。

每个对象都有个 monitor 对象, 加锁就是在竞争 monitor 对象。代码块加锁是在代码块前后分别加上 monitorenter 和 monitorexit 指令来实现的,方法加锁是通过一个标记位来判断的。

20. Lock 和 synchronized 的区别

2a95938ebcf30e92274eb760ed0d2d5f.png

Lock 是 Java 中的接口,可重入锁、悲观锁、独占锁、互斥锁、同步锁。

  • Lock 需要手动获取锁和释放锁。就好比自动挡和手动挡的区别;
  • Lock 是一个接口,而 synchronized 是 Java 中的关键字。synchronized 是 Java 语言内置的实现;
  • synchronized 在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而 Lock 在发生异常时,如果没有主动通过 unLock() 去释放锁,则很可能造成死锁现象,因此使用 Lock 时需要在 finally 块中释放锁;
  • Lock 可以让等待锁的线程响应中断,而 synchronized 却不行。使用 synchronized 时,等待的线程会一直等待下去,不能够响应中断;
  • 通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到;
  • Lock 可以通过实现读写锁提高多个线程进行读操作的效率。

synchronized 的优势:

  • 足够清晰简单,只需要基础的同步功能时,用 synchronized;
  • Lock 应该确保在 finally 块中释放锁。如果使用 synchronized,JVM确保即使出现异常,锁也能被自动释放;
  • 使用 Lock 时,Java 虚拟机很难得知哪些锁对象是由特定线程锁持有的。

21. ReentrantLock 和 synchronized 的区别

34ea70317dbee1aae5bcbeaacd58b026.png

ReentrantLock 是 Java 中的类:继承了 Lock 类,可重入锁、悲观锁、独占锁、互斥锁、同步锁。

划重点

相同点:

  • 主要解决共享变量如何安全访问的问题;
  • 都是可重入锁,也叫做递归锁,同一线程可以多次获得同一个锁;
  • 保证了线程安全的两大特性:可见性、原子性。

不同点:

  • ReentrantLock 就像手动汽车,需要显示的调用 lock 和 unlock 方法, synchronized 隐式获得释放锁;

  • ReentrantLock 可响应中断, synchronized 是不可以响应中断的,ReentrantLock 为处理锁的不可用性提供了更高的灵活性;

  • ReentrantLock 是 API 级别的, synchronized 是 JVM 级别的;

  • ReentrantLock 可以实现公平锁、非公平锁,默认非公平锁。synchronized 是非公平锁,且不可更改;

  • ReentrantLock 通过 Condition 可以绑定多个条件。

推荐阅读  点击标题可跳转Redis 分布式锁的正确实现方式( Java 版 )为什么 ConcurrentHashMap 的读操作不需要加锁?一句话撸完重量级锁、自旋锁、轻量级锁、偏向锁、悲观、乐观锁等各种锁 ---- 不看后悔系列

看完本文有收获?请转发分享给更多人

关注「ImportNew」,提升Java技能

ce1b08f6fcf2ab2d53837ac40a2ecb65.png

好文章,我在看❤️

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

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

相关文章

python大概学多久_自学Python要学多久可以学会?

如果是自学,从零基础开端学习python的话,按照每个人理解能力的不同,大致上需求半年到一年半左右的时刻,当然,如果有其它编程言语的经历,入门还是比较快的,大概需求2~3个月可以用Python言语编写一…

u盘 linux centos 5.3,鸟哥linux私房菜学习笔记,U盘安装centos5.3不能正常进入图形界面的问题...

前面说过自己成功引导了centos系统,现在进入启动界面,首次进入会进行相关设置,按照步骤一步一步完成,取消完光盘安装,点击下一步,就进入下面这个界面,没有登录框。。。没错!怎么蓝屏…

将excel转为python的字典_python读取excel表并把数据转存为字典

excel表如下:我们需要通过使用python的xlrd方法先读取excel,再遍历赋值给字典。代码如下: importxlrdclassRead_Ex():defread_excel(self):#打开excel表,填写路径 book xlrd.open_workbook("../Data/test.xlsx")#找到s…

micropython是啥 知乎_嵌入式开发必备调试工具:Micro-Lab

在工作中,以什么样的方式向领导汇报工作最直接高效呢?当然是图形界面!图形界面更好表达一个程序设计的逻辑思维,一目了然,本次介绍的Micro-Lab出自风媒电子-赵工之手。 1.什么是Micro-Lab? Micro-Lab可以称得上是迄今…

python面向对象三大基本特性_python面向对象之三大特性

继承 先看个简单的例子了解一下继承。class Animal: #父类 def __init__(self, name, age, department): self.namename self.ageage self.departmentdepartmentdefrunning(self):print(‘%s可以奔跑!‘%self.name)class Cat(Animal): #括号里放要继承的父类 def __…

python怎么调用列表_Python中列表的使用

python中的列表与java中的数组非常类似,但使用方法比java中数组简单很多,python中的数据类型不需要显示声明,但在使用时必须赋值,列表元素下标从0开始 初始化列表(初始化一个包含五个元素的列表和一个空列表&#xff0…

jquery find 找到frame select_简述jQuery

jQuery 是一个高效、精简并且功能丰富的 JavaScript 工具库。它提供的 API 易于使用且兼容众多浏览器,这让诸如 HTML 文档遍历和操作、事件处理、动画和 Ajax 操作更加简单。一、获取元素jQuery的核心设计思想就是获取元素,然后对其操作;因此…

求中位数中回文数之和C语言,一些算法题及答案

1. 两数之和给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。示例:给定 nums [2, 7, 11, 1…

自然哲学的数学原理_物理起源点,牛顿《自然哲学的数学原理》

1687年 英国艾萨克牛顿(Sir Isaac Newton,1643—1727),英国物理学家、数学家、天文学家、自然哲学家和炼金术士。1687年他发表《自然哲学的数 学原理》,阐述了万有引力和三大运动定律,奠定了此后三个世纪里力学和天文学的基础&…

如何将另外一个表里的数据与联动_跨境电商(亚马逊)后台财务数据包

亚马逊后台的财务数据包是刚进这个行业的财务人员最希望能了解熟悉的,这块也是相对于国内财务比较有难度的内容,主要难点是亚马逊平台是新的东西,国内财务对规则,费用内容,流程都比较懵,另外就是各项资料都…

无法在源表中获得一组稳定的行_行输出变压器的结构、符号及电路分析

行输出变压器又称逆程变压器、回扫变压器,俗称行输出,它是电视机、显示器中的一个重要变压器。1.行输出变压器结构行输出变压器的全部绕组和高压整流管均密封在其中,底部引出各个绕组的引脚,高压输出采用高压引线直接送至显像管的…

pid调节软件_非常实用的PID算法和PID控制原理

点击箭头处“工业之家”,选择“关注公众号”!PID控制原理和特点工程实际中,应用最为广泛调节器控制规律为比例、积分、微分控制,简称PID控制,又称PID调节。PID控制器问世至今已有近70年历史,它以其结构简单…

对pca降维后的手写体数字图片数据分类_【AI白身境】深度学习中的数据可视化...

今天是新专栏《AI白身境》的第八篇,所谓白身,就是什么都不会,还没有进入角色。上一节我们已经讲述了如何用爬虫爬取数据,那爬取完数据之后就应该是进行处理了,一个很常用的手段是数据可视化。通过数据可视化&#xff0…

android实现箭头流程列表_反思|Android 列表分页组件Paging的设计与实现:系统概述...

作者:却把清梅嗅链接:https://github.com/qingmei2/blogs/issues/30前言本文将对Paging分页组件的设计和实现进行一个系统整体的概述,强烈建议 读者将本文作为学习Paging 阅读优先级最高的文章,所有其它的Paging中文博客阅读优先级…

sql server numeric 可存几位小数_想成为优秀SQL高手?你就差这些细节

标准结构化查询语言(Structured Query Language)简称SQL,sql是我们日常工作中使用最多一项技能,写sql可以说是一个可以干到退休的技能。看似简单,但要精通却很难。 sql包括增、删、改、查,创建表、删除表、修改表等等内容&#xf…

java random产生随机数_java的三种随机数生成方式,必掌握

随机数的产生在一些代码中很常用,也是我们必须要掌握的。而java中产生随机数的方法主要有三种:第一种:new Random()第二种:Math.random()第三种:currentTimeMillis()第一种需要借助java.util.Random类来产生一个随机数…

C 怎么处理windows路径_python学习笔记-7:文件读写之文件与文件路径

文件关键属性:路径文件名根据已知的文件路径生成包含正确路径分隔符的文件路径字符串:import osstt os.path.join(usr,bin,spam)print(stt)#usrbinspamwindows的路径分隔符为,字符串中使用需要转义字符,因此应输入为当前工作目录程序运行时&…

android手机图标 足球球星,世界足坛九大现役球星标志性绰号,第一名所有人都服!...

原标题:世界足坛九大现役球星标志性绰号,第一名所有人都服!9,“大腿”桑切斯自打智利天王桑切斯离开当年无比辉煌的巴萨加盟阿森纳后,就被无数球迷冠以“大腿”的绰号,当然这完全源自于他在英超阿森纳队中极…

mysql 互为主备 宕机 数据丢失_Devops部署-mysql主备多从搭建

​双主多从架构原理介绍IP端口账号密码服务名10.1.1.23306root/syncabc123!数据库A10.1.1.33306root/syncabc123!数据库B10.1.1.43306root/syncabc123!数据库C1.两台mysql都可读写,互为主备,默认只使用一台(masterA)负责数据的写入…

sourcetree mac 免登录_「vscode remote ssh 免登录」mac

如果你只知道传统的ssh操作,或者sftp/ftp上传下载操作,那可能有点太low了。强烈推荐你使用 vscode remote ssh。举个栗子来形象比喻一下:钥匙开门 和 指纹开门 的区别。已经用 vscode remote ssh 功能有很久了,这种体验真的太棒了…