Java锁-Synchronized深层剖析 前言
Java锁的问题,可以说是每个JavaCoder绕不开的一道坎。如果只是粗浅地了解Synchronized等锁的简单应用,那么就没什么谈的了,也不建议继续阅读下去。如果希望非常详细地了解非常底层的信息,如monitor源码剖析,SpinLock,TicketLock,CLHLock等自旋锁的实现,也不建议看下去,因为本文也没有说得那么深入。本文只是按照synchronized这条主线,探讨一下Java的锁实现,如对象头部,markdown,monitor的主要组成,以及不同锁之间的转换。至于常用的ReentrantLock,ReadWriteLock等,我将在之后专门写一篇AQS主线的Java锁分析。
不是我不想解释得更为详细,更为底层,而是因为两个方面。一方面正常开发中真的用不到那么深入的原理。另一方面,而是那些非常深入的资料,比较难以收集,整理。当然啦,等到我的Java积累更加深厚了,也许可以试试。囧
由于Java锁的内容比较杂,划分的维度也是十分多样,所以很是纠结文章的结构。经过一番考虑,还是采用类似正常学习,推演的一种逻辑来写(涉及到一些复杂的新概念时,再详细描述)。希望大家喜欢。
Java锁的相关概念如果让我谈一下对程序中锁的最原始认识,那我就得说说PV操作(详见我在系统架构师中系统内部原理的笔记)了。通过PV操作可以实现同步效果,以及互斥锁等。
如果让我谈一下对Java程序中最常见的锁的认识,那无疑就是Synchronized了。
Java锁的定义那么Java锁是什么?网上许多博客都谈到了偏向锁,自旋锁等定义,唯独就是没人去谈Java锁的定义。我也不能很好定义它,因为Java锁随着近些年的不断扩展,其概念早就比原来膨胀了许多。硬要我说,Java锁就是在多线程情况下,通过特定机制(如CAS),特定对象(如Monitor),配合LockRecord等,实现线程间资源独占,流程同步等效果。
当然这个定义并不完美,但也算差不多说出了我目前对锁的认识(貌似这不叫定义,不要计较)。
Java锁的分类标准自旋锁:是指当一个线程在获取锁的时候,如果锁已经被其他线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环(之前文章提到的CAS就是自旋锁)
乐观锁:假定没有冲突,再修改数据时如果发现数据和之前获取的不一致,则读最新数据,修改后重试修改(之前文章提到的CAS就是乐观锁)
悲观所:假定一定会发生并发冲突,同步所有对数据的相关操作,从读数据就开始上锁(Synchronized就是悲观锁)
独享锁:给资源加上独享锁,该资源同一时刻只能被一个线程持有(如JUC中的写锁)
共享锁:给资源加上共享锁,该资源可同时被多个线程持有(如JUC中的读锁)
可重入锁:线程拿到某资源的锁后,可自由进入同一把锁同步的其他代码(即获得锁的线程,可多次进入持有的锁的代码中,如Synchronized就是可重入锁)
不可重入锁:线程拿到某资源的锁后,不可进入同一把锁同步的其他代码
公平锁:争夺锁的顺序,获得锁的顺序是按照先来后到的(如ReentrantLock(true))
非公平所:争夺锁的顺序,获得锁的顺序并非按照先来后到的(如Synchronized)
其实这里面有很多有意思的东西,如自旋锁的特性,大家都可以根据CAS的实现了解到了。Java的自选锁在JDK4的时候就引入了(但当时需要手动开启),并在JDK1.6变为默认开启,更重要的是,在JDK1.6中Java引入了自适应自旋锁(简单说就是自旋锁的自旋次数不再固定)。又比如自旋锁一般都是乐观锁,独享锁是悲观所的子集等等。
** Java锁还可以按照底层实现分为两种。一种是由JVM提供支持的Synchronized锁,另一种是JDK提供的以AQS为实现基础的JUC工具,如ReentrantLock,ReadWriteLock,以及CountDownLatch,Semaphore,CyclicBarrier等。**
Java锁-SynchronizedSynchronized应该是大家最早接触到的Java锁,也是大家一开始用得最多的锁。毕竟它功能多样,能力又强,又能满足常规开发的需求。
有了上面的概念铺垫,就很好定义Synchronized了。Synchronized是悲观锁,独享锁,可重入锁。
当然Synchronized有多种使用方式,如同步代码块(类锁),同步代码块(对象锁),同步非静态方法,同步静态方法四种。后面有机会,我会挂上我笔记的相关页面。但是总结一下,其实很简单,注意区分锁的持有者与锁的目标就可以了。static就是针对类(即所有对该类的实例对象)。
其次,Synchronized不仅实现同步,并且JMM中规定,Synchronized要保证可见性(详细参照笔记中对volatile可见性的剖析)。
然后Synchronized有锁优化:锁消除,锁粗化(JDK做了锁粗化的优化,但可以通过代码层面优化,可提高代码的可读性与优雅性)
另外,Synchronized确实很方便,很简单,但是也希望大家不要滥用,看起来很糟糕,而且也让后来者很难下叉。
Java锁的实现原理