1.几个概念:
(1)对象头(Mark Word):
在Hotspot虚拟机中,对象在内存中的存储布局分为三块区域:对象头,实例数据、对其填充
对象头包含两部分信息:一是用于存储对象自身的运行时数据,如哈希码、GC分代年龄、锁状态标识、线程持有的锁、偏向线程ID、偏向时间戳等;
对象头的另一部分是;类型指针,即对象指向它的类的元数据的指针,虚拟机通过这个指针来确定这个对象是哪个实例。如果对象是一个数组,则 对象头中还必须要有一块用于记录数组长度的数据(因为虚拟机可以通过普通Java对象的元数据信息确定Java对象的大小,但是从数组的元数据中无法确定数组的大小)
(2)互斥同步:
互斥同步是一种常见的保证并发正确性的手段。同步是指在多个线程并发访问共享数据时,保证共享数据在同一时刻只能被一个线程使用
在Java中,最基本的互斥同步手段是synchronized关键字,该synchronized关键字经过编译之后会在同步块的前后形成monitorenter和monitorexit这两个字节码指令,这两个字节码指令都需要一个renfrence类型的参数来指明锁定和解锁的对象。
在执行monitorenter的时候,虚拟机会首先尝试获取该对象的锁,如果这个对象没有被锁定或者当前线程已经有了那个队形的锁,则吧锁的计数器加1,;在执行monitorexit的时候则吧锁计数器减1。synchronized同步代码块中的锁是可以重入的。
2.几种锁
(1)偏向锁:
偏向锁的目的是消除数据在无竞争情况下的同步原语,进一步挺高程序的性能。大部分的线程是不存在竞争的
偏向锁就是偏心,即偏向锁会偏向于当前已经占有锁的进程
将对象头的锁状态设置为偏向,并将线程ID写入对象头
只要没有竞争,获得偏向锁的进程在将来进入同步块是不需要同步的
当有其他的线程请求相同的锁时,偏向模式结束
启用偏向锁参数 -XX:+UseBiasedLocking 是默认开启的
在竞争激烈时,偏向锁会不停的释放锁,会增加系统负担;所以偏向锁只有在竞争不是很激烈的时候可以提高性能
(2)轻量级锁
轻量级是相对于使用操作系统互斥量量实现传统锁而言的,传统的锁称为重量级锁
代码进入同步块如果此同步对象没有被锁定虚拟机将首先在当前线程的栈帧中创建一个锁记录(Lock Record)的空间,用于存放对象目前对象头的拷贝
然后使用CAS操作尝试将对象的Mark Word更新为指向Lock Record的指针,如果更新成功,则该线程就获得了该对象锁,并且锁标识位将变成00(表示此对象处于轻量级锁定状态);如果更新失败,会检查对象Mark Word是否指向当前栈帧,如果是说明当前线程已经有了这个对象的锁,直接进入同步快,否则说明这个对象锁已经被其他线程占有。
如果两条以上的线程竞争同一个锁,轻量级锁将不再有效,要升级为重量级锁,锁状态标识改为10,Mark Word存储的就是指向重量级锁(互斥量)的指针,后面等待锁的线程将进入阻塞状态
在没有锁竞争的情况下,减少传统锁使用操作系统互斥量产生的性能损耗
在竞争激烈时轻量级锁会做很多的额外操作导致性能下载
(3)自旋锁(竞争发生)
在竞争发生时,如果线程可以很快的获得锁,那么久可以不再操作系统层挂起线程,让线程执行几个空操作(自旋),目的就是已自旋的方法等待锁的过程,避免线程上下文切换,这个过程是要消耗CPU资源的
如果同步快很长,自旋失败,会降低系统性能
如果同步块很短,自旋成功,可以节省线程挂起切换的时间提升性能
注意:
【1】锁技术不是java层面的,而是jvm内部进行的优化
【2】锁优化优先级由低到高:无锁 ,偏向锁 , 轻量级锁,重量级锁;只能升级不能降级
【3】jvm获取锁优化方法和获取锁的步骤:
偏向锁可用,则会首先尝试偏向锁
轻量级锁可用,尝试轻量级锁
以上都失败,则尝试自旋
再失败则尝试普通锁(重量级锁),使用操作系统互斥量在操作系统层挂起
3.锁优化技术
(1)减少持有锁的时间:
将同步方法中不需要进行同步的代码移出,需要同步的可以改成用同步代码块,避免对整个方法同步
(2)减少锁粒度:
将大对象拆成小对象,增加并行度,降低锁竞争
偏向锁、轻量级锁成功率提高
一个基本实现就是ConcurrentHashMap(若干个Segment,对数组中的部分进行加锁);
put操作时,先定位到Segment,锁定一个Segment执行put操作,可以允许多个线程同时进入
还有一个HashMap的同步实现:Collections.synchronizedMap()
(3)锁分离:
根据功能进行分离:ReadWriteLock(包含读锁和写锁,读锁可共享,写锁独占)
读写分离的思想可以延伸,只要是互补影响锁就可以分离
锁分离的一种应用:LinkedBlockingQueue,一端出一端进
(4)锁粗化:
通常情况下,为了保证多线程之间高效并发,会要求每个线程持有锁的时间尽量短,用完公告资源后立即释放锁
(5)无锁:
锁是一种悲观的操作,无锁是一种乐观的操作
无锁的一种实现是CAS,一种非阻塞的同步
参考
http://blog.csdn.net/tanga842428/article/details/52202986
http://blog.csdn.net/wangtaomtk/article/details/52264043
《深入理解jvava虚拟机》