• 锁的基础说明


    synchronized旧版(jdk1.6及之前)

    synchronized也叫互斥锁、悲观锁、同步锁、重量级锁。当多个线程去竞争加锁时,只有一个线程能拿到,其他线程进入到一个阻塞队列,这时候就涉及到操作系统的线程调度,性能会低一些。

    所以它也叫重量级锁,涉及到很多操作系统的支持,有用户态内核态的上下文切换等,耗费性能。

    它本质是java生成的字节码中有一个monitor指令。本质也是对c++代码中的monitor对象加锁。

     CAS

    1、原子性问题。

    去看CAS的compareAndSwapInt方法底层,它是native方法,即c++实现的。里面本质是两步,先比较值是否相等,然后set。我们下载openjdk源码,去找Unsafe.cpp,最后能找到我们真正调用的c++代码。最最核心的就是 Atomic::comxche.

    如果是多核cpu,会使用lock compxchgq。 即多个线程使用CAS,在汇编层面,会有lock指令,会保证compxchgq的命令是加了锁的,保证并发变为串行化。 如果数据少于64bit,就对缓存行加锁,如果数据大就会变为总线锁。 

    所以说CAS虽然叫无锁,实际在汇编和硬件层面还是有锁的。

    2、ABA问题

    在比较并交换的过程中,线程1把A->B,  比较拿到的新值A等于旧值A,就更新为B,但是在这个过程中线程2做了操作,A->C, C->A. 这就是ABA问题。

    解决发放,就是加一个version版本号。

    synchronized优化(jdk1.6之后)

    一开始是无锁,有单线程来了,就升级到偏向锁,如果有多个线程来竞争,就升级为轻量级锁,如果竞争的线程多了,就升级到重量级锁。

    偏向锁

    hotspot作者对大量公司的代码进行试验,发现一个代码块虽然加了synchronized锁,但是大部分时间还是一个线程去运行它。如果我们还跟1.6版本之前一样,要搞一个monitor对象,搞一堆等待队列,还要接触操作系统的互斥量等,就比较浪费。所以jdk1.6之后引入了偏向锁,优化了锁机制。即当一个线程去加锁,把线程id加到锁的内部对象中存着,只要统一线程去获取锁,直接提供,不用进行其他复杂操作。

    轻量级锁

    有多个线程来竞争,就进行CAS自旋去获取。

    重量级锁

    要看内部代码,可能是自旋10次,还有自适应自旋,最后满足条件,就升级到重量级锁。

    对象头中的标示

    对象分为三个部分,对象头中又包含mark word部分。

    mark word占用64bit

    带有synchronized的代码,编译成的字节码会带有monitorenter和monitorexit,当java虚拟机执行到这个指令时,会调用对用的c++的实现,里面源码就有复杂的锁升级过程。

     LongAdder的分段CAS机制

    我们要对一个base值进行++操作。按照synchronized的方式,就是多个线程去竞争,当线程自旋的多了,锁升级为重量级锁。如果使用LongAdder,那么底层就是下面图的方式。

    根据线程数量多少,LongAdder内部为维护一个cell数组,会自动扩容和缩减。大量线程去竞争,会进行分组,一部分修改cell1,一部分修改cell2等等,会后再把cell数组中的值累加。

    这样的计算,比使用atomicInteger的效率还要高。

  • 相关阅读:
    云计算-Linux-查看内核-CPU,内存,网卡,主机名信息,修改主机名
    忘机工尺谱 - 快速打谱软件
    TS编译器选项——指定编译ES版本和模块化使用规范
    java毕业生设计在线果蔬团购系统计算机源码+系统+mysql+调试部署+lw
    【计算机毕业设计】微信小程序:MHK自学平台的设计与实现——后附源码
    c++图像腐蚀操作
    使用PM2部署goweb工程
    家庭记账的最简单方法
    十五、MySQL(DCL)如何实现用户权限控制?
    【Redis】Set类型
  • 原文地址:https://blog.csdn.net/songtaiwu/article/details/125556196