synchronized实现原理
synchronized能够保证方法或者代码块在运行时,同一时刻只有一个方法能够进入到临界区,同时它还能够保证共享变量的内存可见性
Java中每个对象均可以做为锁:
一、静态同步方法,锁是当前类的class文件
二、普通同步方法,锁是当前对象,this
三、同步代码块,锁是括号内的对象
当一个线程访问同步代码块的时候,必须先得到锁,退出或抛出异常的时候要释放锁。
同步代码块中synchronized的实现:html
同步代码块是使用monitorenter和monitorexit指令实现的. monitorenter指令:插入同步代码块的开始位置,监视器进入,获取锁; monitorexit指令:插入同步代码块结束位置,监视器退出,释放锁
任何对象都有一个monitor与之关联,当且一个monitor被持有之后,将处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor全部权,即尝试获取对象的锁。
同步方法:synchronized方法则会被翻译成普通的方法调用和返回执行:invokevirtual、areturn指令,在虚拟机字节码层面没有任何特别的指令来实现被synchronized修饰的方法,而在class文件的方法表中将该方法的access_flags字段中的synchronized标志位置1,表示该方法是同步方法并使用调用该方法的对象或该方法所属的Class在JVM的内部对象表示Kclass做为锁对象。
Java对象头
synchronized用的锁存在Java对象头中。Hotspot虚拟机的对象头主要包括两部分数据:Mark Word(标记字段)、Kclass Pointer(类型指针)。
Kclass Point是对象指向它的类元数据的指针,虚拟机经过这个指针肯定这个对象是哪一个类的实例。
Mark Word用于存储对象自身的运行时数据,它是实现轻量级锁和偏向锁的关键。
Mark Word
Mark Word用于存储对象自身的运行时数据,如哈希码、gc分代年龄、锁状态标志、线程持有的锁、偏向线程ID等。Java对象头通常占两个机器码、若是对象是数组类型,则须要三个机器码,由于JVM虚拟机须要经过Java对象的元数据肯定Java对象的大小,可是没法从数组中的元数据确认数组的大小,因此须要额外一块记录数组的长度。java
Java对象头的存储结构:(32位虚拟机) 25Bit 哈希码 4bit 对象的分代年龄 1bit 是否偏向锁 2bit 锁标志位
对象头信息是与对象自身定义的数据五官的额外存储成本,可是考虑到虚拟机的空间效率,Mark Word被设计成一个非固定的数据结构以便在极小的空间内存存储尽可能多的数据,它会根据对象的状态复用再寄的存储空间,也就是说,Mark Word会随着程序的运行发生改变。
Monitor
Monitor能够理解为一个同步工具,也能够额描述为一种同步机制,一般被描述为一个对象。
全部对象是天生的Monitor,每个对象都有成为Monitor的潜质,在Java设计中,每个对象建立就有一把看不见的锁,它叫作内部锁或Monitor锁。
Monitor是线程私有的数据结构,每个线程都有一个可用的minitor record列表,同时还有一个全局的可用列表。每个被锁住的对象都会和一个minitor关联(对象头的MarkWord中的LockWprd指向monitor的起始地址),同时monitor中有一个Owner字段存放拥有该锁的线程的惟一标识,表示该锁被这个线程占用。数组
Owner:初始为NULL表示当前没有线程拥有该monitor record,当线程成功拥有该锁后保存线程惟一标识,锁释放设置为null EntryQ :关联一个系统互斥锁,阻塞全部试图锁住mointor record失败的线程 RcThis :表示blocked或waiting在该monitor record上的全部线程的个数 Nest :用来实现重入锁的计数 HashCode :保存对象头拷贝过来的哈希值 Candidate:用来避免没必要要的阻塞或等待线程唤醒,由于每一次只有一个线程可以成功拥有锁,若是每次前一个释放锁的线程唤醒全部阻塞或等待的线程,会引发没必要要的上下文切换(从阻塞到就绪而后由于竞争失败又被阻塞)从而致使性能降低。值为0表示没有须要唤醒的线程,1表示要唤醒一个线程竞争锁。
锁的四种状态:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,会随着竞争的激烈逐渐升级。
锁能够升级,不能被降级。
自旋锁
线程的阻塞和唤醒须要CPU从用户态转为核心态,频繁的阻塞和唤醒对CPU是一件负担很重的工做,势必给系统并发带来很大的压力,同时在许多应用上面,对象锁的锁状态只会持续很短的一段时间,为了这段不多的时间频繁阻塞和唤醒锁是很是不值得的,因此引入自旋锁。
自旋锁就是让该线程等待一段时间,不会被当即挂起,看持有锁的线程是否会很快释放锁。这里经过一段无心义的循环进行等待(自旋)
自旋等待不能代替阻塞,虽然能够避免线程切换带来的开销,可是占用了处理器的时间。若是持有锁的线程很快释放了锁,那么自旋的效率就很是好,反之,自旋的线程就会白白消耗掉处理的资源。
可使用-XX:+UseSpinning开启,在JDK1.6中默认开启,默认次数为10,能够经过参数-XX:PreBlockSpin来调整。
适应自旋锁
适应自旋锁就是自旋的次数不是固定的,由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。
线程若是自旋成功了,那么下次自旋的次数就会更加多,由于虚拟机认为上次成功了,这次自旋也颇有可能会再次成功,就会容许自旋等待持续的次数更多。反之减小。
锁消除
为了保证数据的完整性,咱们在进行操做时须要对这部分操做进行同步控制,可是有些状况下,JVM检测到不可能存在共享数据竞争,这时JVM就会对这些同步锁进行消除。
锁消除的依据是逃匿分析的数据支持。
变量是否逃逸,对虚拟机来讲须要使用数据流分析肯定。
可是开发者不清楚,有些时间使用StringBuffer、Vector等的时候会存在隐形的加锁操做,在这里虚拟机会明显检测到能够进行锁消除。
锁粗化
在使用同步锁的时候,须要让同步块的做用范围尽量小,仅在共享数据的实际做用域中进行同步。这样作的目的是为了使须要同步的操做数量尽量缩小。若是存在锁竞争,那么等待锁的线程也能尽快拿到锁。可是一系列的加锁解锁操做,可能会致使没必要要的性能损耗,因此引入锁粗化的概念。
锁粗化就是将多个连续的加锁、解锁链接在一块儿,扩展成一个范围更大的锁。
轻量级锁
引入轻量级锁的目的主要是在没有多线程竞争的前提下,减小传统的重量级锁使用操做系统互斥量产生的性能消耗。当关闭偏向锁功能或多个线程竞争偏向锁致使偏向锁升级为轻量级锁,则会尝试获取轻量级锁。
获取锁:安全
一、判断当前对象是否处于无锁状态(hashcode、0、01),如果,则JVM首先将在当前线程的栈帧中创建一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的Mark Word的拷贝(官方把这份拷贝加了一个Displaced前缀,即Displaced Mark Word);不然执行步骤(3); 二、JVM利用CAS操做尝试将对象的Mark Word更新为指向Lock Record的指正,若是成功表示竞争到锁,则将锁标志位变成00(表示此对象处于轻量级锁状态),执行同步操做;若是失败则执行步骤(3); 三、判断当前对象的Mark Word是否指向当前线程的栈帧,若是是则表示当前线程已经持有当前对象的锁,则直接执行同步代码块;不然只能说明该锁对象已经被其余线程抢占了,这时轻量级锁须要膨胀为重量级锁,锁标志位变成10,后面等待的线程将会进入阻塞状态;
释放锁数据结构
一、取出在获取轻量级锁保存在Displaced Mark Word中的数据; 二、用CAS操做将取出的数据替换当前对象的Mark Word中,若是成功,则说明释放锁成功,不然执行(3); 三、若是CAS操做替换失败,说明有其余线程尝试获取该锁,则须要在释放锁的同时须要唤醒被挂起的线程。
轻量级锁性能提高的依据是对于绝大部分的锁,在整个生命周日内都是不会存在竞争的,若是打破这个依据则除了互斥的开销外,还有额外的CAS操做,所以有多线程竞争的状况下,轻量级锁比重量级锁更慢。
偏向锁
为了在无多线程竞争的状况下尽可能减小没必要要的轻量级锁执行路径(减小没必要要的CAS操做)
获取锁多线程
一、检测Mark Word是否为可偏向状态,便是否为偏向锁1,锁标识位为01 二、若为可偏向状态,则测试线程ID是否为当前线程ID,若是是,则执行步骤(5),不然执行步骤(3); 三、若是线程ID不为当前线程ID,则经过CAS操做竞争锁,竞争成功,则将Mark Word的线程ID替换为当前线程ID,不然执行线程(4); 四、经过CAS竞争锁失败,证实当前存在多线程竞争状况,当到达全局安全点,得到偏向锁的线程被挂起,偏向锁升级为轻量级锁,而后被阻塞在安全点的线程继续往下执行同步代码块; 五、执行同步代码块
释放锁并发
偏向锁的释放采用了一种只有竞争才会释放锁的机制,线程是不会主动去释放偏向锁,须要等待其余线程来竞争。偏向锁的撤销须要等待全局安全点(这个时间点是上没有正在执行的代码)。其步骤以下: 一、暂停拥有偏向锁的线程,判断锁对象石是否还处于被锁定状态; 二、撤销偏向苏,恢复到无锁状态(01)或者轻量级锁的状态;
重量级锁
重量级锁经过对象内部的监视器(monitor)实现,其中monitor的本质是依赖于底层操做系统的Mutex Lock实现,操做系统实现线程之间的切换须要从用户态到内核态的切换,切换成本很是高工具
http://www.importnew.com/23511.html
http://www.javashuo.com/article/p-uvajwpbz-mo.html性能