java并发之synchronized
java实现同步互斥访问有两种方式,synchronized和Lock。
Sychronized是java实现的内置锁,由jvm实现。通过编译Synchronized代码块为字节码可以发现,加锁逻辑被翻译为monitorenter和monitorexit两条指令。Synchronized是对象锁,锁的是monitor对象,对于锁的实现这里不再解释,其由jvm实现,实现原理reentrantlock的原理类似,都是获取不到锁,线程会被挂起在一个队列中。
1、编译字节码
javap -verbose Xxx
注:Xxx为字节码文件,不需要带.class
2、例子
可以看出同步代码快的上下被monitorenter和monitorexit两条指令包裹,分别代表加锁和解锁。
同时,synchronzed的在1.5版本后还有以一个大优化,就是锁的膨胀升级。在早期版本,synchronized会直接加一把重量级锁。等待锁的线程会被挂起,线程的挂起和激活涉及到从用户态和内核态之间的转换,耗时耗力。在新版本中,synchronized提供了三层锁的升级,从无锁--》偏向锁--》轻量级锁--》重量级锁。
1、锁标识
java对象头中有一段Mark word区域,在锁膨胀的过程中,这段空间会标识不同的含义。
2、synchronized锁升级过程
当线程a来争夺锁的时候,首先对象升级为偏向锁。线程a通过cas操作尝试将锁对象的mark word的线程id设置为自己。
如果a还没释放锁的时候,线程b来争夺锁。b通过cas尝试获取锁,失败后,b会申请将锁升级为轻量级锁。线程a会找一个程序安全点,暂停线程,将锁升级为轻量级锁。线程a会将mark word的值拷贝到线程栈中,并且将锁对象的mark word修改为指向其线程栈中锁记录的指针。
此时线程b会通过自旋来尝试获取锁,若一定次数没有获取,就会申请将锁升级为重量级锁,同时进入阻塞状态。等线程a释放锁后,再将线程b唤醒,开启一段新的锁争夺。