zoukankan      html  css  js  c++  java
  • 并发编程:深入理解synchronized

    synchronize的使用场景

    线程安全问题:多线程对共享数据状态的访问没有控制

    用锁(互斥)来控制对共享数据的访问

    synchronized是虚拟机级别提供给我们的同步关键字

    synchronized的使用

    1、修饰实例方法(锁是当前对象)
    2、修饰静态方法(锁是当前类的字节码对象)
    3、修饰代码块(锁是括号()中指定的对象)
    总结:锁都是锁对象,不管是.class对象还是具体的某个实例对象。当两个线程调用同一把锁的代码时,会串行化执行。

    synchronized到底在什么地方加了锁?

    存在对象头里。
    对象分为三部分:对象头,实例数据,对齐填充(虚拟机要求8字节最小单位)。
    那么对象头有哪些信息呢?MarkWord、KlassWord、数组长度;其中Markword记录了锁的信息,如锁标记、偏向锁标记、锁状态、线程ID(偏向锁)还有其他信息如gc标记、分代年龄、hashcode等等。KlassWord主要存储对象的指针相关信息。

    synchronized 锁的升级

    1.6之前synchronized都是重量级锁,在1.6版本做了大量优化。锁既要保证数据安全性,又要尽量的保证性能。
    重量级锁:直接调用操作系统挂起线程(内核态->用户态),重量级锁由重量级锁指针指向的ObjectMonitor实现,监视器对象存储了该对象被获取锁的次数(有加有减)、重入次数、持有锁的线程、阻塞队列、等待队列等。
    无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁
    偏向锁:单线程访问,偏向锁记录线程ID为该线程,过来直接执行
    轻量级锁:两个线程发生了竞争的时候,会升级成轻量级锁。(当旧的偏向线程被销毁、或者当前没有持有锁,则不会升级,变成无锁)。轻量级锁是自旋等待
    重量级锁:自旋次数过多;或者来了第三个线程,则会升级成重量级锁,这时候没拿到锁的线程直接阻塞
    锁只可以升级不可以降级,但是偏向级锁可以置为无锁状态。

    偏向锁的升级

    线程1去查看对象头里是否有线程1ID,假如无锁则CAS替换为线程1,有锁则CAS获取
    CAS:乐观锁。CompareAndSwap(value, expect, update),比较expect和value是否一样,假如不一样,则替换失败;假如一样,则成功替换。(比较和替换必须是一个原子操作)
    CAS的缺点:存在ABA问题,可以用版本号解决,java里用AtomicStampedReference解决;不断的CAS会消耗CPU比较大
    替换成功后,线程1执行,此时来了线程2,线程2 CAS获取偏向锁失败,此时线程2暂停线程1,撤销偏向锁,升级为轻量级锁

    轻量级锁的升级

    轻量级锁中,两个线程会不断自旋,有锁的时候CAS将MarkWord替换成轻量级锁,替换成功的则持有锁。
    线程1会在栈帧中分配Lock Reocrd空间存储MarkWord的拷贝,通过CAS操作 并将对象头MarkWord改成指向Lock reocrd的指针,然后将Lock Reocrd的owner指针指向object mark word。
    自旋次数过多或者第三个线程来了会膨胀成重量级锁。
    自旋次数:设置自旋次数、自适应自旋(自动调整)

    重量级锁

    每一个对象都存在一个ObjectMonitor对象。重量级锁通过ObjectMonitor(互斥锁)实现。
    该锁直接挂起阻塞的锁。
    当线程1获得重量级锁时,线程2会进去监视器的阻塞队列。当线程1释放锁的时候,会唤醒阻塞队列中的线程。

  • 相关阅读:
    indy Sftp 编程 ftp安全访问
    关于MySql里的字段
    php---魔术方法(__tostring(),__set_state())
    看了这个才发现jQuery源代码不是那么晦涩
    JS的Document属性和方法小结
    JS的Document属性和方法
    原始JS选择器使用方法总结
    docker 镜像配置
    Docker部署SpringBoot项目
    springboot 和spring cloud 博客分享
  • 原文地址:https://www.cnblogs.com/fcb-it/p/13282740.html
Copyright © 2011-2022 走看看