最近在学习java编程知识,在这里记录下关于sychornized的理解
1.sychornized对使用者透明,调用者并不知道JVM获取锁的流程及其种类。
2.默认加锁顺序为偏向锁、轻量级锁、重量级锁,偏向锁默默认2秒后自动开启,可通过XX:BiasedLockingStartupDelay=0禁用延迟,通过XX:- UseBiasedLocking=false关闭偏向锁。
4.sychornized可作用与代码块以及方法声明处,当作用于代码块,程序编译为字节码指令后对应monitorenter、monitorexit两条指令,作用于方法声明时,JVM并未提出具体实施方案。
3.加锁流程如下:
当执行到sychornized对应字节码指令时(Thread1),开始加锁,默认偏向锁,偏向锁无法自动撤销,此时sychornized中对象的对象头中MarkWord字段通过CAS操作记录加锁线程的线程ID,该线程ID为操作系统线程ID,并非JVM中线程的线程ID。上述操作完成后,即代表加锁完毕。
Thread2执行到sychornized处,判断该锁是否为偏向锁,若是且与Thread1并未发生竞争,则锁重偏向为Thread2,若重偏向次数超过20次,则进行批量重偏向。若与Thread1发生竞争,将偏向锁升级为轻量级锁。重偏向超过40次,则撤销所有偏向锁,之后程序无法使用偏向锁。生成轻量级锁流程如下:
在Thread1线程栈帧中创建锁记录对象,锁记录对象包括锁对象引用与锁记录对象引用两部分,之后通过CAS操作交换锁记录对象引用与锁对象MarkWoed字段,进行上述操作后,轻量级锁创建完毕,若之后Thread1重复对锁记录加锁,则发生锁重入,依然在栈帧中创建锁记录对象,但是锁记录对象引用字段值为null,目的为记录锁重入次数,当sychornized执行完毕,进行轻量级锁撤销。将重入的锁记录对象撤销,第一次创建的锁记录中保留的锁对象MarkWord字段重新写入到锁对象中,然后清空该锁记录对象,撤销完毕。
Thread2在Thread1持有轻量级锁时竞争锁,发生锁膨胀,轻量级锁升级为重量级锁流程如下:
Thread2申请系统创建的Moitor对象,进入该对象阻塞队列中,将Monitor对象owner字段值设置为Thread1
Thread在sychornized代码执行完毕后进行解锁,由于锁以及升级,故只能进入重量级锁解锁流程,即找到Monitor对象,将owner设置为null,同时唤醒阻塞队列中的线程,解锁完毕。
4.细节补充:
可调用hashcode方法关闭偏向状态,因为哈希码与偏向锁中记录线程ID的字段重复,空间不够。
解锁、加锁、竞争时通过判断MarkWord字段最后两位来确定当前是否已经加锁以及加的是什么锁。
偏向锁、轻量级锁目的在于线程不发生竞争时使用,当发生竞争,只能使用重量级锁。
线程创建后,会维护一个线程队列,从而判断哪些线程存活。
JVM有代码优化功能,当加的锁没有意义时,会优化代码为未加锁,从而提高性能,加锁会极大地降低系统的性能,能不加锁,就不加锁。