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,一经查实,立即删除!

相关文章

linux安装icc步骤,怎麼安装不到 icc?

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼我是用 arch linux, 安装的 icc 版本是 2011.11.339(ia32 x86_64)错误如下:Step no: 5 of 6 | Installation--------------------------------------------------------------------------------Each component will be installed…

利用python制作漂亮的词云图_利用python制作漂亮的词云图

新建一个新的目录随便找个地方新建一个目录,比如我在F盘下面新建一个word_cloud文件夹(名字都是随便取),并且打开这个文件夹.把刚刚下载的msyh.ttc复制到这里.新建一个my_word_cloud.py在这里.然后可以在my_word_cloud.py写代码了,首先导入相关的包##对于NLP(自然语言处理)来说…

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

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

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

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

偏移shaderuv_Unity Shader 之 uv动画

Unity 动画Unity Shader 内置时间变量引入时间变量名称类型描述_Timefloat4t是自该场景加载开始所经过的时间,4个分量分别是(t/20, t, 2t, 3t)_SinTimefloat4t是时间的正弦值,(t/8, t/4, t/2, t)_CosTimefloat4t是时间的余弦值,(t/8, t/4, t/…

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

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

linux中的改变bin级别,Linux常用命令

关机命令:shutdown -h now / shutdown -r now / reboot切换图形化界面:startx 切换到命令行:注销即可添加用户:useradd xiaoming设置密码:passwd xiaoming删除用户:userdel xiaoming 【userdel -r x…

pythonencoding etf-8_etf iopv python 代码30个Python常用小技巧

1、原地交换两个数字x, y 10, 20print(x, y)y, x x, yprint(x, y)10 2020 102、链状比较操作符n 10print(1 print(1 > n < 9)TrueFalse3、使用三元操作符来实现条件赋值[表达式为真的返回值] if [表达式] else [表达式为假的返回值]y 20x 9 if (y 10) else 8print(x…

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

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

win10树莓派改ip_Window 10通过网线和Wifi连接树莓派

几个月前买了个树莓派&#xff0c;扔在一边没有捣鼓&#xff0c;今天搞定了笔记本通过家里的wifi登录树莓派&#xff0c;下面列出设置过程。实验环境:网络&#xff1a;只有wifi材料&#xff1a;笔记本一台(Win10)&#xff0c;树莓派一台&#xff0c;EDUP USB无线网卡一枚&#…

双机通信c语言程序,双机通信(C语言、主机和从机共用程序)

//头文件#include//循环移位文件#define uchar unsigned char//宏定义#define uint unsigned intsbit key1P3^5; //位声明uchar code table[]{0X00,0x3f,0x06,0x5b,//数码管显示的数值0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};uchar table_tr[6];//暂存最后按下的六个数值uchar…

python读取大文件目录_65.Python读取大文件

方式一 import os path r"C:\Users\yzt\Desktop\work\InfosecTestPlatform\libs\Log\mainline1-nohup.log" ##################### """ file.seek(off, whence0)&#xff1a; 从文件中移动off个操作标记&#xff08;文件指针&#xff09;&#xff0c…

java设计按月每天签到_java实现app签到功能

本文实例为大家分享了java实现app签到功能的具体代码&#xff0c;供大家参考&#xff0c;具体内容如下1.首先设计二张表&#xff0c;第一张表sign_calc记录用户连续签到次数&#xff0c;字段id,user_id,continue_days&#xff0c;第二张表sign_detail签到详情表id,user_id,sign…

c语言第四版课后答案第三章3.4,算法与数据结构C语言版课后习题答案(机械工业出版社)第3,4章 习题参考答案...

第3章栈和队列一、基础知识题3.1 有五个数依次进栈&#xff1a;1&#xff0c;2&#xff0c;3&#xff0c;4&#xff0c;5。在各种出栈的序列中&#xff0c;以3&#xff0c;4先出的序列有哪几个。(&#xff13;在&#xff14;之前出栈)。【解答】34215 &#xff0c;34251&#x…

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

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

用python做透视表_用Python实现数据的透视表的方法

用Python实现数据的透视表的方法来源&#xff1a;中文源码网 浏览&#xff1a; 次 日期&#xff1a;2019年11月5日【下载文档: 用Python实现数据的透视表的方法.txt 】(友情提示:右键点上行txt文档名->目标另存为)用Python实现数据的透视表的方法在处理数据时&#x…

c语言编程用进退法求搜索区间代码,用c对函数进行优化的问题

//多维无约束优化软件设计#include #include #include double det1e-5; //计算精度double det11e-3; //梯度判断精度double ak3e-3; //搜索步长double dx1e-4; //梯度计算步长/*目标函数(n维)入口参数&#xff1a;x &#xff1a;n维数组&#xff0c;自变量返回值 &#xff1a;函…

python关闭指定浏览器页面_Python自动关闭浏览器关闭网页的方法

在py编辑窗口中输入代码&#xff1a; from selenium.webdriver.chrome.options import Options from selenium import webdriver __browser_url rC:\Users\Administrator\AppData\Roaming\360se6\Application\360se.exe chrome_options Options() chrome_options.binary_loca…

ubuntu 安装kde桌面_在Ubuntu 20.04系统上安装KDE Plasma Desktop的方法

本文介绍在Ubuntu 20.04系统上安装KDE Plasma Desktop的方法。Plasma Desktop是KDE创建的一种流行且功能强大的桌面环境&#xff0c;主要用于Linux系统。KDE Plasma的当前版本是5&#xff0c;它是KDE Plasma 4的后续版本&#xff0c;旨在帮助您以高效的方式完成工作。当Plasma强…

c语言将字母的acsaii,C语言上机实验 答案

《C语言上机实验 答案》由会员分享&#xff0c;可在线阅读&#xff0c;更多相关《C语言上机实验 答案(31页珍藏版)》请在装配图网上搜索。1、实验一 上机操作初步(2学时)一、实验方式&#xff1a;一人一机二、实验目的&#xff1a;1、熟悉VC语言的上机环境及上机操作过程。2、了…