synchronized、ReentrantLock、volatile
名词解释
- synchronized
是Java中的关键字,是一种同步锁,可以修饰代码块,方法,静态的方法,类。synchronized(Object) 不能用String常量、Integer、 Long。 - ReentrantLock
是一种同步锁,可以实现公平锁机制,获取锁和释放锁都需要手动操作。 - volatile
是Java中的关键字,保障可见性,有序性,并不能保证原子性。- 可见性
当线程t1开始运行的时候,会把running值从内存中读到t1线程的工作区,在运行过程中直接使用这个copy,并不会每次都去读取堆内存,这样,当主线程修改running的值之后,t1线程感知不到,所以不会停止运行,使用volatile,将会强制所有线程都去堆内存中读取running的值。 - 有序性
volatile关键字能禁止指令重排序,所以volatile能在一定程度上保证有序性。 - 原子性
不可分割,最小执行单位。
- 可见性
synchronized和ReentrantLock区别
synchronized是独占锁,加锁和解锁的过程自动进行,易于操作,但不够灵活;
ReentrantLock也是独占锁,加锁和解锁的过程需要手动进行,不易操作,但非常灵活。synchronized可重入,因为加锁和解锁自动进行,不必担心最后是否释放锁;
ReentrantLock也可重入,但加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁。synchronized不可响应中断,一个线程获取不到锁就一直等着;ReentrantLock可以响应中断。
ReentrantLock可以实现公平锁机制,synchronized不行。
synchronized和volatile区别
- volatile保证线程的可见性,有序性,但是不能保证其原子性;synchronized保证线程的原子性、可见性,但是不能保证其有序性。
synchronized源码解析
Java对象组成及Synchronized锁存放位置
对象是放在堆内存中的,对象大致可以分为三个部分,分别是对象头,实例变量和填充字节。
- 对象头,主要包括两部分1. Mark Word (标记字段),2.Klass Pointer(类型指针)。Klass Point 是对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例(即指向方法区类的模版信息)。Mark Word用于存储对象自身的运行时数据。
- 实例变量,存放类的属性数据信息,包括父类的属性信息,这部分内存按4字节对齐。
- 填充字节,由于虚拟机要求对象起始地址必须是8字节的整数倍。填充数据不是必须存在的,仅仅是为了字节对齐。
假如有如下的类,a=100这个信息就存储在实例变量中
public class Test {
int a = 100;
}
填充数据主要是为了方便内存管理,如你想要10字节的内存,但是会给你分配16字节的内存,多出来的字节就是填充数据
synchronized不论是修饰方法还是代码块,都是通过持有修饰对象的锁来实现同步,那么synchronized锁对象是存在哪里的呢?答案是存在锁对象的对象头Mark Word,来看一下Mark Word存储了哪些内容?
由于对象头的信息是与对象自身定义的数据没有关系的额外存储成本,因此考虑到JVM的空间效率,Mark Word 被设计成为一个非固定的数据结构,以便存储更多有效的数据,它会根据对象本身的状态复用自己的存储空间,也就是说,Mark Word会随着程序的运行发生变化,变化状态如下 (32位虚拟机):
锁升级过程
在JDK1.6之前,synchronized都是重量级锁,1.6版本之后,进行了锁的升级;锁可以升级但不能降级,但是偏向锁状态可以被重置为无锁状态。
无锁
没有线程运行时,此时处于无锁状态。偏向锁
当线程1访问代码块并获取锁对象时,会在java对象头和栈帧中记录偏向的锁的threadID,因为偏向锁不会主动释放锁,因此以后线程1再次获取锁的时候,需要比较当前线程的threadID和Java对象头中的threadID是否一致,如果一致(还是线程1获取锁对象),则无需使用CAS来加锁、解锁;如果不一致(其他线程,如线程2要竞争锁对象,而偏向锁不会主动释放因此还是存储的线程1的threadID),那么需要查看Java对象头中记录的线程1是否存活,如果没有存活,那么锁对象被重置为无锁状态,其它线程(线程2)可以竞争将其设置为偏向锁;如果存活,那么立刻查找该线程(线程1)的栈帧信息,如果还是需要继续持有这个锁对象,那么暂停当前线程1,撤销偏向锁,升级为轻量级锁,如果线程1 不再使用该锁对象,那么将锁对象状态设为无锁状态,重新偏向新的线程。自旋锁(轻量级锁)(CAS)(Comapre And Swap)
线程1获取轻量级锁时会先把锁对象的对象头MarkWord复制一份到线程1的栈帧中创建的用于存储锁记录的空间(称为DisplacedMarkWord),然后使用CAS把对象头中的内容替换为线程1存储的锁记录(DisplacedMarkWord)的地址。
如果在线程1复制对象头的同时(在线程1CAS之前),线程2也准备获取锁,复制了对象头到线程2的锁记录空间中,但是在线程2CAS的时候,发现线程1已经把对象头换了,线程2的CAS失败,那么线程2就尝试使用自旋锁来等待线程1释放锁。 自旋锁简单来说就是让线程2在循环中不断CAS。
但是如果自旋的时间太长也不行,因为自旋是要消耗CPU的,因此自旋的次数是有限制的,比如10次或者100次,如果自旋次数到了线程1还没有释放锁,或者线程1还在执行,线程2还在自旋等待,这时又有一个线程3过来竞争这个锁对象,那么这个时候轻量级锁就会膨胀为重量级锁。重量级锁把除了拥有锁的线程都阻塞,防止CPU空转。重量级锁
线程1持有对象锁时,线程2到达竞争该对象锁,但是线程1还没有释放锁,线程2将自旋,如果线程2自旋次数达到10次,将升级为重量级锁,将正在运行的线程除外都加入队列中,防止空旋,消耗CPU资源。重量级锁调用的是内核,轻量级锁和偏向锁不需要调用内核,是在内存中进行处理。
几种锁的优缺点
参考:https://blog.csdn.net/zzti_erlie/article/details/103997713
AbstractQueuedSynchronizer(AQS)和Synchronized区别
Synchronized
- Synchronized关键字在底层的C++实现中,存在两个重要的数据结构(集合):WaitSet和EntryList。
- WaitSet中存放的是调用了Object的Wait方法的线程对象(被封装成了C++的Node对象)。
- EntryList中存放的是陷入阻塞状态,需要获取moniter的那些个线程对象。
- 当一个线程被notify后,它就会从WaitSet中移动到EntryList中去。
- 当进入到EntryList后,该线程依然需要与其他的线程竞争moniter对象。
- 如果争抢到了,就表示该线程获取到了对象的锁,它可以以排他的方式对应的执行同步代码。
AQS
- AQS中存在两个队列,分别是condition对象上的条件队列,以及AQS本身的阻塞队列。
- 这两个队列中的每一个对象都是Node实例(里面封装了线程对象)。
- 当condition条件队列中的线程被signal后,该线程就会从条件队列中被转移到AQS阻塞队列中去。
- 位于AQS阻塞队列中的Node对象本质上都是由一个双向链表去构成的(CLH)
- 在获取AQS锁的时候,这些进入到阻塞队列中的线程会按照在队列中的排序先后尝试的去获取锁(公平锁)。
- 当阻塞队列中的线程获取锁后,就表示改线程可以正常执行了
- 陷入到阻塞状态的线程,依然需要进入到操作系统内核态,进去阻塞(park方法)。
参考
https://blog.csdn.net/Ypopstar/article/details/106898129;
https://www.cnblogs.com/waterystone/p/4920797.html。
synchronized、ReentrantLock、volatile的更多相关文章
- 高级java必会系列二:多线程经常使用的3个关键字:synchronized、ReentrantLock、volatile
系列一讲解了多线程,本章讲解多线程开发中经常使用到的3个关键字synchronized.ReentrantLock.volatile. 一.synchronized 互斥锁,即操作互斥,并发线程过来, ...
- Lock、ReentrantLock、synchronized、ReentrantReadWriteLock使用
先来看一段代码,实现如下打印效果: 1 2 A 3 4 B 5 6 C 7 8 D 9 10 E 11 12 F 13 14 G 15 16 H 17 18 I 19 20 J 21 22 K 23 ...
- 同步中的四种锁synchronized、ReentrantLock、ReadWriteLock、StampedLock
目录 1.synchronized同步锁 2.ReentrantLock重入锁 3.ReadWriteLock读写锁 4.StampedLock戳锁(目前没找到合适的名字,先这么叫吧...) 5.总结 ...
- 同步中的四种锁synchronized、ReentrantLock、ReentrantReadWriteLock、StampedLock
为了更好的支持并发程序,JDK内部提供了多种锁.本文总结4种锁. 1.synchronized同步锁 使用: synchronized本质上就2种锁: 1.锁同步代码块 2.锁方法 可用object. ...
- 201709020工作日记--synchronized、ReentrantLock、读写锁
1.reentrantLock java.util.concurrent.lock 中的Lock 框架是锁定的一个抽象,它允许把锁定的实现作为 Java 类,而不是作为语言的特性来实现.这就为Lock ...
- synchronized、Lock、ReentrantLock、ReadWriteLock
synchronized:同步锁,是java内置的关键字.当一个线程A执行到被synchronized修饰的方法时,其他线程B如果也要执行这个方法,那么B只能等A执行完方法释放锁后才能获取资源锁执行s ...
- Java并发编程总结3——AQS、ReentrantLock、ReentrantReadWriteLock(转)
本文内容主要总结自<Java并发编程的艺术>第5章——Java中的锁. 一.AQS AbstractQueuedSynchronizer(简称AQS),队列同步器,是用来构建锁或者其他同步 ...
- Java并发编程总结3——AQS、ReentrantLock、ReentrantReadWriteLock
本文内容主要总结自<Java并发编程的艺术>第5章——Java中的锁. 一.AQS AbstractQueuedSynchronizer(简称AQS),队列同步器,是用来构建锁或者其他同步 ...
- Lock、ReentrantLock、ReentrantReadWriteLock区别
Lock Lock相比于synchronized具有更强大的功能,在jdk1.6之前,锁竞争激烈的情况下使用lock的实现类ReentrantLock甚至比synchronized具有更好的性能,1. ...
随机推荐
- Spring Boot 使用 Filter
Filter 是 JavaEE 中 Servlet 规范的一个组件,位于包javax.servlet 中,它可以在 HTTP 请求到达 Servlet 之前,被一个或多个Filter处理. 1. 编写 ...
- MongoDB笔记:windows环境安装及连接本地数据库
下载MongoDB 2.4.9版 mongodb官网下载:http://www.mongodb.org/downloads 直接下载地址:http://fastdl.mongodb.org/win32 ...
- Exponential family of distributions
目录 定义 性质 极大似然估计 最大熵 例子 Bernoulli 指数分布 正态分布 Choi H. I. Lecture 4: Exponential family of distributions ...
- vue递归过滤树结构数组
let arr=[{ title:'1', key:'1', type:0, children:[{ title:'1-1', key:'1-1', type:0, }] },{ title:'2', ...
- 2021前端面试css(三)
overflow 原理 块格式化上下文是css可视化渲染的一部分,它是一块区域,规定了内部块盒的渲染方式,以及浮动相互之间的影响关系,当元素设置了overflow 样式且值不为visible时,元素就 ...
- Qos 0/1/2的理解
Qos 0/1/2的理解 Qos 0 最多一次的传输 消息是基于TCP/IP网络传输的.没有回应,在协议中也没有定义重传的语义.消息可能到达服务器1次,也可能根本不会到达. Qos 1 至少一次的传输 ...
- BUG—Nuget包版本不一致导致程序行为与预期不符
注:本文收录于<Bug集锦>,请点击此处查看全文目录 BUG起因 先介绍一下背景: 数周前的一个极其平常的下午,完成了本次迭代的开发工作,发布到QA提测,然后开始摸鱼.没几分钟,测试就来找 ...
- hisql 高级功能数据检测将错误数据拦截在系统外 一
hisql github源码下载 git clone https://github.com/tansar/HiSql.git 在设计第二范式数据库时经常会把可能重复的数据单独做一种表关联,但是在写入表 ...
- Springboot+Javamail实现邮件发送
Springboot+Javamail实现邮件发送 使用的是spring-context-support-5.2.6.RELEASE.jar里的javamail javamail 官方文档:javam ...
- Django在使用logging日志模块时报错无法操作文件 logging error Permission Error [WinError 32]
产生原因: 这个问题只会在开发的时候遇到,而且配置是写入到setting.py配置文件,我们定义了日志文件大小,当日志满了的时候,这时候就会遇到这个问题, 因为在使用Pycharm运行django的时 ...