博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JAVA锁机制的总结
阅读量:3576 次
发布时间:2019-05-20

本文共 2356 字,大约阅读时间需要 7 分钟。

锁的分类

    主要分类包括乐观锁及悲观锁;从另一个角度来说也可以分为公平锁及非公平锁,synchronized机制的锁是非公平锁,这一点是从竞争机制来说,对某个锁的获得不是先到先得,有可能后来者居上(自璇锁)。

锁的实现机制

    JAVA中锁机制的实现主要有两种,一种是基于JVM层面的synchronized 另一种是基于JAVA语言层面的Lock。

synchronized的实现原理剖析

    用法

        synchronized是可重入不可中断的。synchronized的用法主要有三种。基于静态方法,基于实例方法,基于代码块(需显示指定锁的对象)。

    细节原理背景

        对象监视器(monitor)

            每一个java对象都会有一个与之关联的监视器,该监视器的生命周期可视为与该对象一致,同生共死。可认为,获得该监视器即获得该对象的锁。监视器的基本数据结构有一个waitSet(wait()),一个entrySet(竞争该对象,处于阻塞状态),Owner(线程ID,标识谁拥有monitor)。notify ,wait,notifyAll的底层实现也是基于monitor的

        Java对象头

            Java 对象在堆中存储实例可以分为三个部分:对象头,实例数据,填充数据。其中对象会存储一些与锁相关的信息,根据对象的不同阶段,对象头信息会动态变化。具体信息可参照下图

锁的实现

    对于同步代码块

        对于代码块是通过两条JVM指令来实现的monitorenter 及 monitorexit

    对于同步方法

        对于同步方法,是用过隐式标识来实现,方法区的常量池中,有标识能够认定改方法是静态方法,然后加以控制。

其实无论是同步代码块抑或是同步方法,本质都是通过对象来关联对象监视器进行实现的。执行线程将都先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。在方法执行期间,其他任何线程都无法再获得同一个monitor对象。 其实本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。

锁的优化

    JDK 1.6以前synchronized是基于操作系统的Mutex Lock来实现的。这种实现涉及到内核态及用户态的频繁切换,性能较低,故称之为重量级锁。在1.6之后,进行了诸多的优化。注意一点的是,锁的等级是只可升,而不可降级。这样做是为了提高锁的获得和释放的效率

    偏向锁

         发现,很多情况下,某个同步方法只会由同一个线程使用,这时候要是加锁,对系统的性能是不利的,于是引入了偏向锁。引入了偏向锁,对象的对象头的状态会发现改变,并且会记录该线程的线程Id.。下次线程再使用的时候,只需简单对比一下该线程Id是否为自己即可。偏向锁的释放,需要有竞争,否则该锁是长期拥有的。偏向锁,适用于某一同步方法长期时间下,仅会被同一线程所调用。

    轻量级锁。

    自旋锁

        线程的阻塞与唤醒需要cpu从用户态切换到内核态,频繁的切换对cpu来说是一个很大负担,而且我们发现有些线程的阻塞只是很短的时间,于是引入自璇锁,让线程执行一些空循序。

重量级锁

    重量锁就是基于的(Monitor),它很像C中的Mutex,除了具备Mutex(0|1)互斥的功能,它还负责实现了Semaphore(信号量)的功能,也就是说它至少包含一个竞争锁的队列,和一个信号阻塞队列(wait队列),前者负责做互斥,后一个用于做线程同步。

    锁消除

            比如在局部变量中StringBuffer 的锁会被消除,与StringBuilder一致。     

对于Lock锁机制实现原理的剖析

    Lock是基于Java语言实现的,是乐观锁,自带公平和非公平两种模式,并且是可重入可中断的。

    Lock的基本API

 

  • void lock() 获取锁,调用该方法当前线程将会获取锁,当锁获取后,该方法将返回。
  • void lockInterruptibly() throws InterruptedException 可中断获取锁,与lock()方法不同之处在于该方法会响应中断,即在锁的获取过程中可以中断当前线程
  • boolean tryLock() 尝试非阻塞的获取锁,调用该方法立即返回,true表示获取到锁
  • boolean tryLock(long time,TimeUnit unit) throws InterruptedException 超时获取锁,以下情况会返回:时间内获取到了锁,时间内被中断,时间到了没有获取到锁。
  • void unlock() 释放锁        

主要实现原理

CHL(双向链表,FIFO,存储线程信息)

   所有无法获得锁的线程,都需入队列.。唤醒时首先唤醒队列头的线程

 CAS(进入队列,及出队列,更新状态信息的原子操作基本原理)

LockSupport.park()

    调用操作系统相关(mutuxe)阻塞线程,可指定阻塞时长,唤醒后需重新参与锁竞争。

LockSupport.unpark(Thread thread)

    调用操作系统相关(mutuxe)唤醒阻塞的线程

Synchronized与Lock的对比  

 当然Lock比synchronized更适合在应用层扩展,可以继承AbstractQueuedSynchronizer定义各种实现,比如实现读写锁(ReadWriteLock),公平或不公平锁;同时,Lock对应的Condition也比wait/notify要方便的多、灵活的多。

 

参考资料:

 

各种锁的详细介绍  :https://www.cnblogs.com/wade-luffy/p/5969418.html

锁的膨胀与升级:https://blog.csdn.net/choukekai/article/details/63688332

 

你可能感兴趣的文章
[LeetCode javaScript] 804. 唯一摩尔斯密码词
查看>>
[LeetCode javaScript] 476. 数字的补数
查看>>
[LeetCode javaScript] 811. 子域名访问计数
查看>>
[LeetCode javaScript] 414. 第三大的数
查看>>
[LeetCode javaScript] 242. 有效的字母异位词
查看>>
[LeetCode javaScript] 75. 颜色分类
查看>>
[LeetCode javaScript] 56. 合并区间
查看>>
[LeetCode javaScript] 190. 颠倒二进制位
查看>>
[LeetCode javaScript] 521. 最长特殊序列 Ⅰ
查看>>
[LeetCode javaScript] 806. 写字符串需要的行数
查看>>
[LeetCode javaScript] 824. 山羊拉丁文
查看>>
[LeetCode javaScript] 463. 岛屿的周长
查看>>
[LeetCode javaScript] 107. 二叉树的层次遍历 II
查看>>
[LeetCode javaScript] 637. 二叉树的层平均值
查看>>
[LeetCode javaScript] 1. 两数之和
查看>>
[LeetCode javaScript] 14. 最长公共前缀
查看>>
[LeetCode javaScript] 26. 删除排序数组中的重复项
查看>>
[LeetCode javaScript] 8. 字符串转换整数 (atoi)
查看>>
[LeetCode javaScript] 28. 实现strStr()
查看>>
cv2.error: OpenCV(3.4.2) c:\projects\opencv-python\opencv\modules\imgproc\src\color.hpp:25
查看>>