Java中的锁机制 -- 乐观锁、悲观锁、自旋锁、可重入锁、读写锁、公平锁、非公平锁、共享锁、独占锁、重量级锁、轻量级锁、偏向锁、分段锁、互斥锁、同步锁、死锁、锁粗化、锁消除
文章目录
- 1. Java中的锁机制
- 1.1 乐观锁
- 1.2 悲观锁
- 1.3 自旋锁
- 1.4 可重入锁(递归锁)
- 1.5 读写锁
- 1.6 公平锁
- 1.7 非公平锁
- 1.8 共享锁
- 1.9 独占锁
- 1.10 重量级锁
- 1.11 轻量级锁(可膨胀为重量级锁:锁膨胀)
- 1.12 偏向锁
- 1.13 分段锁
- 1.14 互斥锁
- 1.15 同步锁
- 1.16 死锁
- 1.17 锁粗化
- 1.18 锁消除
- 1.19 synchronized
- 1.20 Lock和synchronized的区别
- 1.21 ReentrantLock 和synchronized的区别
1. Java中的锁机制
序号 | 名称 | 应用 |
---|---|---|
1 | 乐观锁 | CAS(Compare And Set) |
2 | 悲观锁 | synchronized、vector、hashtable |
3 | 自旋锁 | CAS(Compare And Set) |
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.1 乐观锁
乐观锁是一种乐观思想,假定当前环境是读多写少,遇到并发写的概率比较低,读数据时认为别的线程不会正在进行修改(所以没有上锁)。写数据时,判断当前与期望 值是否相同,如果相同则进行更新(更新期间加锁,保证是原子性的)。
Java中的乐观锁:CAS(Compare And Set),比较并替换,比较当前值(主内存中的值),与预期值(当前线程中的值,主内存中值的一份拷贝)是否一样,一样则更新,否则继续进行CAS操作。
如上图所示,乐观锁可以同时进行读操作,读的时候其他线程不能进行写操作。
1.2 悲观锁
悲观锁是一种悲观思想,即认为写多读少,遇到并发写的可能性高,每次去拿数据的 时候都认为其他线程会修改,所以每次读写数据都会认为其他线程会修改,所以每次读写数据时都会上锁。其他线程想要读写这个数据时,会被这个线程block,直到这个线程释放锁然后其他线程获取到锁。
Java中的悲观锁:synchronized
修饰的方法和方法块、ReentrantLock
锁。
如上图所示,只能有一个线程进行读操作或者写操作,其他线程的读写操作均不能进行(被阻塞)。
1.3 自旋锁
自旋锁是一种技术: 为了让线程等待,我们只须让线程执行一个忙循环(自旋)。 现在绝大多数的个人电脑和服务器都是多路(核)处理器系统,如果物理机器有一个 以上的处理器或者处理器核心,能让两个或以上的线程同时并行执行,就可以让后面请求锁的那个线程稍等一会,但不放弃处理器的执行时间,看看持有锁的线程是否很快就会释放锁。
- 自旋锁的优点: 避免了线程切换的开销。挂起线程和恢复线程的操作都需要转入内核 态中完成,这些操作给
Java
虚拟机的并发性能带来了很大的压力。 - 自旋锁的缺点: 占用处理器的时间,如果占用的时间很长,会白白消耗处理器资源, 而不会做任何有价值的工作,带来性能的浪费。因此自旋等待的时间必须有一定的限度,如果自旋超过了限定的次数仍然没有成功获得锁,就应当使用传统的方式去挂起线程。
- 自旋次数默认值:
10
次,可以使用参数-XX:PreBlockSpin
来自行更改。 - 自适应自旋: 自适应意味着自旋的时间不再是固定的,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定的。有了自适应自旋,随着程序运行时间的增长 及性能监控信息的不断完善,虚拟机对程序锁的状态预测就会越来越精准。
Java
中的自旋锁:CAS
操作中的比较操作失败后的自旋等待。
1.4 可重入锁(递归锁)
可重入锁是一种技术: 任意线程在获取到锁之后能够再次获取该锁而不会被锁所阻塞。
可重入锁的原理: 通过组合自定义同步器来实现锁的获取与释放。
- 再次获取锁:识别获取锁的线程是否为当前占据锁的线程,如果是,则再 次成功获取。获取锁后,进行计数自增。
- 释放锁:释放锁时,进行计数自减。
Java中的可重入锁: ReentrantLock
、synchronized
修饰的方法或代码段。
可重入锁的作用: 避免死锁。
可重入锁常见问题:
问:可重入锁如果加了两把,但是只释放了一把会出现什么问题?
答:程序会卡死,线程不能出来,也就是说我们申请了几把锁,就需要释放几把锁。
问:如果只加了一把锁,释放两次会出现什么问题?
答:会报错,抛出java.lang.IllegalMonitorStateException
异常。
1.5 读写锁
读写锁是一种技术: 通过ReentrantReadWriteLock
类来实现。为了提高性能, Java
提供了读写锁,在读的地方使用读锁,在写的地方使用写锁,灵活控制,如果没有写锁的情况下,读是无阻塞的,在一定程度上提高了程序的执行效率。 读写锁分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,这是由 JVM
自己控制的。
读锁: 允许多个线程获取读锁,同时访问同一个资源。
写锁: 只允许一个线程获取写锁,不允许同时访问同一个资源。
1.6 公平锁
公平锁是一种思想: 多个线程按照申请锁的顺序来获取锁。在并发环境中,每个线程会先查看此锁维护的等待队列,如果当前等待队列为空,则占有锁,如果等待队列不 为空,则加入到等待队列的末尾,按照FIFO的原则从队列中拿到线程,然后占有锁。
1.7 非公平锁
非公平锁是一种思想: 线程尝试获取锁,如果获取不到,则再采用公平锁的方式。多 个线程获取锁的顺序,不是按照先到先得的顺序,有可能后申请锁的线程比先申请的线程优先获取锁。
- 优点: 非公平锁的性能高于公平锁。
- 缺点: 有可能造成线程饥饿(某个线程很长一段时间获取不到锁)
- Java中的非公平锁:
synchronized
是非公平锁,ReentrantLock
通过构造函数指定该锁是公平的(ReentrantLock(true)
)还是非公平的(ReentrantLock(false)
),默认是非公平的。
1.8 共享锁
共享锁是一种思想: 可以有多个线程获取读锁,以共享的方式持有锁。和乐观锁、读 写锁同义
Java
中用到的共享锁:ReentrantReadWriteLock
。
1.9 独占锁
独占锁是一种思想: 只能有一个线程获取锁,以独占的方式持有锁。和悲观锁、互斥锁同义。
Java中用到的独占锁: synchronized
,ReentrantLock
。
1.10 重量级锁
重量级锁是一种称谓:synchronized
是通过对象内部的一个叫做监视器锁 (monitor)来实现的,监视器锁本身依赖底层的操作系统的 Mutex Lock
来实现。操 作系统实现线程的切换需要从用户态切换到核心态,成本非常高。这种依赖于操作系 统 Mutex Lock 来实现的锁称为重量级锁。为了优化synchonized
,引入了轻量级锁, 偏向锁。
Java
中的重量级锁: synchronized
1.11 轻量级锁(可膨胀为重量级锁:锁膨胀)
轻量级锁是JDK6
时加入的一种锁优化机制: 轻量级锁是在无竞争的情况下使用CAS操作去消除同步使用的互斥量。轻量级是相对于使用操作系统互斥量来实现的重量级锁而言的。轻量级锁在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。如果出现两条以上的线程争用同一个锁的情况,那轻量级锁将不会有效,必须膨胀为重量级锁。
- 优点: 如果没有竞争,通过CAS操作成功避免了使用互斥量的开销。
- 缺点: 如果存在竞争,除了互斥量本身的开销外,还额外产生了CAS操作的开销,因此在有竞争的情况下,轻量级锁比传统的重量级锁更慢。
1.12 偏向锁
偏向锁是JDK6时加入的一种锁优化机制: 在无竞争的情况下把整个同步都消除掉, 连CAS
操作都不去做了。偏是指偏心,它的意思是这个锁会偏向于第一个获得它的线程,如果在接下来的执行过程中,该锁一直没有被其他的线程获取,则持有偏向锁的线程将永远不需要再进行同步。持有偏向锁的线程以后每次进入这个锁相关的同步块时,虚拟机都可以不再进行任何同步操作(例如加锁、解锁及对 Mark Word 的更新 操作等)。
- 优点: 把整个同步都消除掉,连
CAS
操作都不去做了,优于轻量级锁。 - 缺点: 如果程序中大多数的锁都总是被多个不同的线程访问,那偏向锁就是多余的。
1.13 分段锁
分段锁是一种机制: 最好的例子来说明分段锁是ConcurrentHashMap
。
ConcurrentHashMap原理:它内部细分了若干个小的 HashMap
,称之为段 (Segment
)。 默认情况下一个 ConcurrentHashMap
被进一步细分为 16
个段,既就是锁的并发度。如果需要在 ConcurrentHashMap
添加一项key-value
,并不是将整个 HashMap
加锁,而是首先根据 hashcode
得到该key-value
应该存放在哪个段 中,然后对该段加锁,并完成 put
操作。在多线程环境中,如果多个线程同时进行put
操作,只要被加入的key-value
不存放在同一个段中,则线程间可以做到真正的并行。
线程安全:ConcurrentHashMap
是一个 Segment
数组, Segment
通过继承 ReentrantLock
来进行加锁,所以每次需要加锁的操作锁住的是一个 segment
,这 样只要保证每个 Segment
是线程安全的,也就实现了全局的线程安全
1.14 互斥锁
互斥锁与悲观锁、独占锁同义,表示某个资源只能被一个线程访问,其他线程不能访问。
- 读-读互斥
- 读-写互斥
- 写-读互斥
- 写-写互斥
1.15 同步锁
同步锁与互斥锁同义,表示并发执行的多个线程,在同一时间内只允许一个线程访问共享数据。
Java中的同步锁: synchronized
1.16 死锁
可以认为是两个线程或进程在请求对方占有的资源。
线程死锁描述的是这样⼀种情况:多个线程同时被阻塞,它们中的⼀个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。
如下图所示,线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对⽅的资源,所以这两个线程就会互相等待⽽进⼊死锁状态。
Java中的死锁不能自行打破,所以线程死锁后,线程不能进行响应。所以一定要注意程序的并发场景,避免造成死锁。
1.17 锁粗化
锁粗化是一种优化技术: 如果一系列的连续操作都对同一个对象反复加锁和解锁,甚至加锁操作都是出现在循环体之中,就算真的没有线程竞争,频繁地进行互斥同步操作将会导致不必要的性能损耗,所以就采取了一种方案:把加锁的范围扩展(粗化)到整个操作序列的外部,这样加锁解锁的频率就会大大降低,从而减少了性能损耗。
1.18 锁消除
锁消除是一种优化技术: 就是把锁干掉。当Java
虚拟机运行时发现有些共享数据不会被线程竞争时就可以进行锁消除
那如何判断共享数据不会被线程竞争?
利用逃逸分析技术:分析对象的作用域,如果对象在A
方法中定义后,被作为参数传递到B
方法中,则称为方法逃逸;如果被其他线程访问,则称为线程逃逸。 在堆上的某个数据不会逃逸出去被其他线程访问到,就可以把它当作栈上数据对待, 认为它是线程私有的,同步加锁就不需要了。
可以参考Java内存分析
1.19 synchronized
synchronized
是Java
中的关键字:用来修饰方法、对象实例。属于独占锁、悲观锁、 可重入锁、非公平锁。
- 作用于实例方法时,锁住的是对象的实例(
this
); - 当作用于静态方法时,锁住的是
Class
类,相当于类的一个全局锁,会锁 所有调用该方法的线程; synchronized
作用于一个非NULL
的对象实例时,锁住的是所有以该对象为锁的代码块。 它有多个队列,当多个线程一起访问某个对象监视器的时候,对象监视器会将这些线程存储在不同的容器中。
每个对象都有个 monitor
对象, 加锁就是在竞争 monitor
对象,代码块加锁是在代码块前后分别加上 monitorenter
和 monitorexit
指令来实现的,方法加锁是通过一个标记位来判断的。
1.20 Lock和synchronized的区别
Lock: 是Java中的接口,可重入锁、悲观锁、独占锁、互斥锁、同步锁。
Lock
需要手动获取锁和释放锁。Lock
是一个接口,而synchronized
是Java
中的关键字,synchronized
是内置的语言实现。synchronized
在发生异常时,会自动释放线程占有的锁,因此不会导致 死锁现象发生,而Lock
在发生异常时,如果没有主动通过unLock()
去释放 锁,则很可能造成死锁现象,因此使用Lock
时需要在finally
块中释放锁。Lock
可以让等待锁的线程响应中断,而synchronized
却不行,使用synchronized
时,等待的线程会一直等待下去,不能够响应中断。- 通过
Lock
可以知道有没有成功获取锁,而synchronized
却无法办到。 Lock
可以通过实现读写锁提高多个线程进行读操作的效率。
synchronized的优势:
- 足够清晰简单,只需要基础的同步功能时,用
synchronized
。 Lock
应该确保在finally
块中释放锁。如果使用synchronized
,JVM
确保即 使出现异常,锁也能被自动释放。- 使用
Lock
时,Java
虚拟机很难得知哪些锁对象是由特定线程锁持有的。
1.21 ReentrantLock 和synchronized的区别
ReentrantLock
是Java
中的类 : 继承了Lock
类,可重入锁、悲观锁、独占锁、互斥 锁、同步锁。
相同点
- 主要解决共享变量如何安全访问的问题。
- 都是可重入锁,也叫做递归锁,同一线程可以多次获得同一个锁。
- 保证了线程安全的两大特性:可见性、原子性。
不同点
ReentrantLock
就像手动汽车,需要显示的调用lock
和unlock
方法,synchronized
隐式获得释放锁。ReentrantLock
可响应中断,synchronized
是不可以响应中断的,ReentrantLock
为处理锁的不可用性提供了更高的灵活性。ReentrantLock
是API
级别的,synchronized
是JVM
级别的。ReentrantLock
可以实现公平锁、非公平锁,默认非公平锁。synchronized
是非公平锁,且不可更改。ReentrantLock
通过Condition
可以绑定多个条件
Java中的锁机制 -- 乐观锁、悲观锁、自旋锁、可重入锁、读写锁、公平锁、非公平锁、共享锁、独占锁、重量级锁、轻量级锁、偏向锁、分段锁、互斥锁、同步锁、死锁、锁粗化、锁消除相关推荐
- 云阶月地,关锁千重(一.公平和非公平)
看到文章的标题是不是很诧异,一个搞技术的为什么要搞这么文艺的话题呢?标题说关锁千重,是不是很形象,我们在开发中的锁不也是多种多样么? Lock 既然之前说了锁千重,那锁到底有多少种,他们的分类又是怎么 ...
- Linux 锁机制(3)之自旋锁
Linux 锁机制(3)之自旋锁 1. 自旋锁 1.1 两种锁 1.2 自旋锁 1.3 自旋名字来源:自旋锁一直循环等待,直到获取锁为止. 1.4 自旋锁优点: 2 自旋锁特点/使用: 2.1 临界区 ...
- Java锁之公平和非公平锁
Java锁之公平和非公平锁 目录 公平锁和非公平锁概念 公平锁和非公平锁区别 ReentrantLock和synchronized是公平锁还是非公平锁? 1. 公平锁和非公平锁概念 公平锁:是指多个线 ...
- java并发编程(三十五)——公平与非公平锁实战
前言 在 java并发编程(十六)--锁的七大分类及特点 一文中我们对锁有各个维度的分类,其中有一个维度是公平/非公平,本文我们来探讨下公平与非公平锁. 公平|非公平 首先,我们来看下什么是公平锁和非 ...
- 公平锁和非公平锁-ReentrantLock是如何实现公平、非公平的
转载:https://www.jianshu.com/p/5104cd94dbe0 1.什么是公平锁与非公平锁 公平锁:公平锁就是保障了多线程下各线程获取锁的顺序,先到的线程优先获取锁. 非公平锁:非 ...
- 【面试篇】Java多线程并发-Java中的CAS机制算法
Java中的CAS机制算法 a.CAS例子 再讲解CAS机制之前,先来看一道经典的并发执行1000次递增的问题: public class Test { public static int count ...
- 浅说Java中的反射机制(一)
在学习传智播客李勇老师的JDBC系列时,会出现反射的概念,由于又是第一次见,不免感到陌生.所以再次在博客园找到一篇文章,先记录如下: 引用自java中的反射机制,作者bingoideas.(()为我手 ...
- 【Java面试题】21 Java中的异常处理机制的简单原理和应用。
[Java面试题]21 Java中的异常处理机制的简单原理和应用. 参考文章: (1)[Java面试题]21 Java中的异常处理机制的简单原理和应用. (2)https://www.cnblogs. ...
- java代码安全检测机制_全面解析:java中的反射机制,内含代码验证解析
什么是反射? 在运行状态中,对于任意一个类,都能够获取到这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性(包括私有的方法和属性),这种动态获取的信息以及动态调用对象的方法的功 ...
最新文章
- 10个有趣的javascript和css库(2019年5月最新)
- 4.3.8 DHCP协议
- centos 配置yum源
- 服务器部署java网站,服务器部署java网站
- 如何优雅而又不失内涵的在centos7下安装tree命令
- mysql innodb myisam 混合,MySQL MyIsam/InnoDB混合在一起的事务
- 【转】.NET Core 可移植类库PCL Portable Class Library详解
- 在windows xp下编译出ffmpeg.exe
- LeetCode 672. 灯泡开关 Ⅱ(枚举)
- DTCoreText
- Mybatis的SQL注入
- spring + ehcache 配置
- 电子书,电子图书馆网址大全
- 图神经网络——node2vec
- ISSCC上的微型计算机:体积更小、功耗更少、算力更强
- 【python 保存生成的图片 (plt;opencv;PIL)】
- Referrer Policy:strict-origin-when-cross-origin 404
- 如何正确的将拼多多的买家晒图采集并保存
- Kafka producer程序本地运行时发送信息失败解决方案
- 模拟登陆115网盘(MFC版)
热门文章
- 香港中文大学(深圳)李海洲/王本友教授招收大模型方向的博士生(2023Fall/2024SpringFall),研究助理和博后
- WebGL简易教程(十):光照
- QT vector转QVector(来自stackflow)
- 大年初六,你最期待大数据与哪个行业的结合有所突破?
- Mac版本的After Effects 2023中英文切换方法
- 极速高清——给你带来全新的高清视野
- python 两种版本在cmd命令行切换
- php 循环电泳函数,双向凝胶电泳操作步骤
- 已重置应用默认设置html,Win10提示已重置应用默认设置怎么办?Win10已重置应用默认设置的解决方法...
- Linux之用户管理