JDK 1.6对synchronized进行了大量优化,使其不那么重了。如为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁。
Java中的每一个对象都可以作为锁,有以下三种形式:
(1):对于普通方法,锁是当前实例对象
(2):对于静态同步方法,锁是当前类的Class对象
(3):对于同步代码块,锁是synchronized括号里的对象
当一个线程试图访问同步代码块时,它必须先得到锁,退出或抛出异常时必须释放锁。那么锁存放在哪里呢?锁里面会存储什么信息呢?
JVM基于进入和退出Monitor对象来实现方法同步和代码块同步,代码块同步是通过monitorenter和monitorexit指令实现的。
monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法的结束处和异常处,JVM要求每个monitorenter必须有对应的monitorexit与之配对。
每个对象都有一个monitor与之关联,是每个对象与生俱来的隐藏字段,线程获取锁时,会根据锁对象头中的monitor状态进行加锁判断,如果monitor为0,就可以加锁持有锁,并将monitor置为1,如果当前线程已经持有了monitor,那么monitor继续加1(可重入),线程执行完毕释放锁则将monitor减1,直至monitor为0,则锁释放成功;如果monitor为1,表示锁已经被其他线程持有,则线程将处于BLOCKED阻塞状态。
总结:
Synchronized是JVM来实现的,JVM底层是通过监视锁来实现synchronized同步的。监视锁即monitor,是每个对象与生俱来的一个隐藏字段。通过在代码编译时,在同步代码块的前插入monitorenter指令和在代码块结束处和异常处插入monitorexit指令,分别对锁对象头中的monitor字段+1和-1实现加锁和释放锁的。
如果在进入同步代码块获取锁时,锁对象头中的monitor字段的值为0,那么则可以获取锁,并将monitor字段置为1,执行结束退出同步代码块时将monitor置为0释放锁。若当前线程已经持有了当前对象锁,则再次获取锁时,monitor继续加1,释放锁时monitor减1,直至monitor为0时,锁释放成功。
如果在获取锁时,锁对象的monitor为1,则表示锁已经被其他线程持有,则当前线程将出于阻塞状态。
END.