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. ...
随机推荐
- 手把手一步一步教你使用Java开发一个大型街机动作闯关类游戏01游戏窗口
项目源码 项目源码 游戏配置信息类 Config.java 没什么解释的. package config; public class Config { public final static Stri ...
- 编写Java程序,实现对兵营类的封装,将兵营类中的所有属性设置为私有访问权限,方法设置为公有访问权限
返回本章节 返回作业目录 需求说明: 实现对兵营类的封装 将兵营类中的所有属性设置为私有访问权限. 将兵营类中所有属性的赋值方法设置为公有访问权限. 要求兵营名称的长度在4-8位之间. 要求兵营士兵的 ...
- 编写Java程序,创建Dota游戏中的兵营类,兵营类有一个类成员变量count、一个实例变量name和另一个实例变量selfCount。
返回本章节 返回作业目录 需求说明: 创建Dota游戏中的兵营类 兵营类有一个类成员变量count.一个实例变量name和另一个实例变量selfCount. count表示的是兵营已经创建士兵的总数: ...
- 部署Kubernetes Cluster
中文学习网站:https://www.kubernetes.org.cn/doc-16 部署docker服务 所有节点部署docker服务 curl -sSL https://get.daocloud ...
- 大厂必问的Spring面试题
准备了一个月的八股文,经历了二十几场秋招面试之后,发现Spring很受面试官青睐.最近有空将Spring常见的面试题总结了一下,希望对大家有所帮助. 文章目录: Spring的优点 Spring 用到 ...
- PHP 的扩展类型及安装方式
扩展类型 底层扩展(基于C语言): PECL 上层扩展(基于PHP 语言): PEAR Composer PECL # 查找扩展 $ pecl search extname # 安装扩展 $ pecl ...
- CF858D Polycarp's phone book
题意翻译 有 n 个长度为 9 且只包含数字字符互不相同的串. 需要对于每个串找到一个长度最短的识别码,使得这个识别码当且仅当为这个串的子串. 题目分析 因为范围不是非常大,所以可以将子串筛出来 然后 ...
- Linux根目录缺少x权限,产生的两个错误
错误一:root用户执行systemctl命令报误 [root@node1 ~]# systemctl restart sshd * (pkttyagent:10364): WARNING *: Un ...
- Docker 部署 ElasticSearch-Head 及其他插件
拉取ElasticSearch-Head镜像 docker pull mobz/elasticsearch-head:5 运行ElasticSearch-Head容器 docker run -d -- ...
- 如何在 CentOS 上安装 dos2unix 和 unix2dos 命令
yum install -y dos2unix 注意:以上安装包既包含 dos2unix 命令,又包含 unix2dos 命令.