JAVA锁机制的总结

锁的分类

    主要分类包括乐观锁及悲观锁;从另一个角度来说也可以分为公平锁及非公平锁,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

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值