名词解释

  • 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。

最新文章

  1. django 同步数据库
  2. iOS 开发笔记 -- 各种细枝末节的知识(水滴石穿)
  3. linux root不能用
  4. 使用opencv自带的融合函数
  5. 小白日记28:kali渗透测试之Web渗透-扫描工具-Nikto
  6. Cocos2d-x学习笔记之Cocos2d-x开发环境搭建
  7. 构建服务端的AMD/CMD模块加载器
  8. 配置oracle账号密码永不过期
  9. iOS app 集成友盟推送问题
  10. unity中怎样获取全部子物体的组件
  11. JAVA的HashTable源码分析
  12. 中国大学MOOC-陈越、何钦铭-数据结构-2015秋 01-复杂度2 Maximum Subsequence Sum (25分)
  13. 静默获取微信用户openid如此简单,只需要一句话
  14. Spark源码剖析 - 任务提交与执行
  15. 第五周学习总结-HTML5
  16. 发现一个强大的可视化第三方库pyecharts
  17. Cython的简单使用
  18. java Date型时间比较大小
  19. three.js学习:三维空间下的直线
  20. [ 测试思维 ] 启发式测试策略模型(HTSM)

热门文章

  1. CS5266代替AG9311|Type C转HDMI带PD3.0转换芯片|AG9311替代方案
  2. 基于Spring MVC + Spring + MyBatis的【学生信息管理系统】
  3. 论文翻译:2020_Acoustic Echo Cancellation Based on Recurrent Neural Network
  4. emqx的一个配置参数
  5. HAproxy开启日志记录
  6. 【计理05组01号】R 语言基础入门
  7. Mysql 性能优化记录
  8. 初识python 之 爬虫:爬取中国天气网数据
  9. python 之 pip、pypdf2 安装与卸载
  10. docker安装elasticsearch6.8.3-单机模式及可视化Kibana6.8.3