synchronized的实现和锁优化,
synchronized实现。
在同步代码块的前后会生成两条字节码指令,monitorenter,monitorexit。
当执行monitorenter指令,如果获取到锁对象,那么就将锁的计数器+1,获取不到对象,就会进入阻塞状态。
当执行monitorexeit指令,将锁的计数器-1,计数器为0的时候,就会释放锁。
重量级锁:
线程在获取不到锁的时候,线程就进入阻塞状态,直到锁被释放,线程被唤醒。
线程阻塞唤醒,这些操作涉及操作系统调用,要从操作系统的用户态切换到内核态。开销大。
锁优化:
自旋:
获取不到锁,在处理器空跑,并询问锁是否被释放。缺点是,浪费cpu资源,进入阻塞的线程获取锁的优先级降低。
自适应自旋,根据以前获取锁的经历,如果曾经获取到锁,那么自旋时间就短一些,否则时间长一些。
轻量级锁:
重量级锁,要不断的在内核态和用户态切换,开销比较大。
轻量级锁可以避免线程在内核态和用户态中切换,减少性能的开销。(根据使用经验,数据在同步周期内都是不存在数据竞争的)
实现方法:
java对象头包含:标记字段(mark word),类型指针。标记字段其中有两位是存储锁标志位。01未锁定,00轻量级锁定,10膨胀重量级锁定,11GC,根据标志位的不同,mark word存储了不同的内容。
当线程要获取锁的时候,会把锁对象的mark work复制到线程的栈帧中的一个空间(锁记录),
加锁的时候,jvm会使用CAS将mark word更新为指向锁记录的执行,如果更新成功,则获取锁(mark word标志位更新为00),更新不成功,如果已经是当前线程获取,那么继续执行后续代码,如果当前线程没有持有锁,那么锁就会膨胀为重量级锁(10)。
解锁的时候, Mark word替换回锁记录数据,如果替换成功,同步过程完成,如果替换不成功,则说明其他线程持有锁,在释放锁的同时,会唤醒其他线程。
偏向锁:
就是访问代码块不加锁,直到有其他线程来获取锁,才会对代码块进行加锁。
当锁对象第一次被线程获取后,会将mark word的标志位设置为01,然后将线程的id,记录在mark word中。持有偏向锁的线程进入同步代码块,不会进行加锁,当有其他线程尝试获取锁,此时就会撤销偏向锁,变成未锁定状态,或者轻量级锁。
原文:https://www.cnblogs.com/haiqichen/p/13693010.html