• 网站首页
  • 微服务
  • 设计模式
  • 并发编程
  • 源码分析
  • 中间件
  • 数据库
  • 文献
  • 工具
  • 其他
  • Synchronize深入源码分析,Java锁你能知道有多少?

    发布时间: 2019-12-19 17:41首页:架构技术精选 > 源码分析 > 阅读()

    前言: 

    synchronize会使用,但是对于深层次的知识,不是很清楚,故整理一篇博客。

     

    简介:

    能够保证在同一时刻,最多只有一个线程执行该端代码,以达到保证并发安全效果。

     

    两种用法:

    对象锁

    包括方法锁(默认锁对象为this当前实例对象) 锁某个方法

    同步代码块锁(自己制定锁对象) 锁某块代码

    类锁

    指synchronized修饰静态的方法或指定锁为Class对象

    概念:Java类可能有很多个对象,但只有一个Class对象。

    本质:所谓的类锁,不过是Class对象的锁而已。



     

    对线程访问同步方法的7种情况:

     

      1. 两个线程同时访问一个对象的同步方法

     

      2. 两个线程访问的是两个对象的同步方法

     

      3. 两个线程访问的是synchronized的静态方法:静态方法带synchronized

     

      4. 同时访问同步方法和非同步方法,非同步方法不受影响

     

      5. 访问同一个对象的不同的普通同步方法

     

      6. 同时访问静态synchronized和非静态synchronized方法

     

      7. 方法抛异常后,会释放锁

     

    小结核心:

     

      1. 一把锁只能同时被一个线程获取,没有拿到锁的线程必须等待(1,5情况)

     

      2. 每个实例都对应有自己的一把锁,不同实例之间互不影响。例如:锁对象是.class以及synchronized修饰的是static方法的时候,所有对象共用同一把锁(对应第2,3,4,6情况)

     

      3. 不论是方法正常执行完毕或者方法抛出异常,都会释放锁(7情况)

     

    synchronize性质:

     

    可重入       方法可以重入,父子类可以重入

    不可中断     一旦这个锁已经被被人获得了,如何我还想获得,我只能选择等待或者阻塞,知道别的线程释放这个锁。而Lock类,拥有中断能力。第一点,如果我觉得我等待时间长了。有权中断现在已经获取到锁的线程的执行。第二点,如果我觉得我不想等待了,可以退出。

    原理:

     

    加锁原理

    可重入原理

    可见性原理
     

    调试方法:


    可以看到线层的状态:

     

    原理:

     

    加锁和释放锁的原理:

    现象:每一个类的实例对应一把锁,每一个synchronized方法都必须获得调用该方法的实例的锁才可以执行,否则阻塞。

    我们指定对象就可以了,锁的释放获取由JVM去自动实现。

    获取和释放锁的时机:内置锁

    每一个Java对象都可以用作实现一个同步的锁,这个锁称之为内置锁(监视器锁)。线程在家进入代码块之前,会自动获取这个锁。退出代码块时候会自动释放锁。

    获得这个内置锁的唯一途径,就是进入到这个锁保护的代码块或者方法中。

     

    加锁和释放锁的原理:

    深入JVM每一个对象都有个对象头,可以存储很多信息。

    Monditorenter 和 Monditorexit指令

    (联想操作系统知识:临界区)

    每个对象都与Monitor相关联,而一个Monitor的lock锁只能被一个线程同一时间获得。

    一个线程尝试获得与这个对象关联的Monitor所有权到时候,只会发生以下三种:

    1. Monitor计数器为0,目前还没有被获得,线程获取后计数器加1.

    2. 如果Monitor已经拿到了锁的所有权,又重入了。这样导致计数器累加。

    3. 如果Monitor已经被其他线程持有了。我去获取时候,只能阻塞状态了。知道技术器变为0,再去尝试获取锁。

    关于Monditorexit:

    释放对于Monitor的所有权,释放过程就是将Monitor计数器减1.如果减完是0,当前线程不再拥有对Monitor的所有权了,其他的阻塞线程会再次尝试获取对该把锁的所有权,不是0这是可重入进来的,可以继续持有这把锁。

    可重入原理:加锁次数计数器

    JVM负责跟踪对象被加锁的次数。每个对象都含有一把锁,JVM负责跟踪对象被加锁的次数。

    线程第一次给对象加锁的时候,技术变为1.每当这个相同的线程在此对象上再次获得锁时,计数会递增。

    每当任务离开时候,技术递减,当计数为0到时候,锁被完全释放。

     

    synchronized缺陷:

    效率低:锁的释放情况少,视图获得锁时候不能设置超时,不能中断一个正在试图获得锁的线程。

    不够灵活(读写锁更灵活):加锁和释放的时机单一,每个锁仅有单一的条件(某个对象),可能不够 。

    无法知道是否成功获取到锁

    反编译:javap    -verbose  xx.class

     

    问题总结:

    1. 使用注意点:

    锁对象不能为空(锁信息保存在头部,对象都没有,怎么玩儿),作用域不宜过大(包裹的代码块),避免死锁。

    2. 如何选择Lock和synchronized关键字

     

    思考:

    1. 多个线程等待同一个synchronized锁的时候,JVM如何选择下一个获取锁的是哪个线程?等待时间最长,随机?

    2. Synchronized使得同时只有一个线程可以执行,性能较差,有什么办法可以提升性能?优化使用范围使用其它类型的锁,读写锁

    3. 我想更灵活的控制锁的获取和释放(现在释放锁的时机都规定死了),怎么办?自己实现lock接口,可控

    4. 什么是锁的升级,降级。什么是JVM里的偏向锁,轻量级锁,重量级锁?之前的版本中synchronized性能不是很好,经过后面的迭代。利用偏斜锁,轻量级锁,重量级锁。JVM根据synchronized关键字锁使用的次数和种种指标来对锁进行非常有效的优化。还涉及到对象头里面的一些字段。

     

    总结:JVM会自动通过使用monitor来加锁和解锁,保证了同时只有一个线程可以执行指定代码,从而保证了线程安全,同时具有可重入和不可中断性质。

     

    MonitorEnter表示进入对象的监视器当中 

    特别声明:文章内容仅供参考,不造成任何投资建议。投资者据此操作,风险自担。

    网站首页 - 微服务 - 设计模式 - 并发编程 - 源码分析 - 中间件 - 数据库 - 文献 - 工具 - 其他

    本站不良内容举报联系客服QQ:483966038 官方微信:MYKT-xiaowei 服务热线:

    未经本站书面特别授权,请勿转载或建立镜像

    Copyright © 2016-2020 架构技术精选 版权所有 XMl地图