synchronized底层的原理,跟jvm指令和monitor有关系
如果用到了synchronized关键字,在底层编译后的jvm指令中,会有monitorenter和monitorexit两个指令
monitorenter
// 代码对应的指令
monitorexit
每个对象都会关联一个monitor,比如一个对象实例就有一个monitor,一个类的Class对象也有一个monitor,如果要对这个对象加锁,那么必须获取这个对象关联的monitor的lock锁
monitor里会有一个计数器,从0开始,如果一个线程想要获取monitor锁,看它的计数器是否为0,如果为0,那么说明锁空闲,它就可以获取锁了,然后计数器加1
另外monitor的锁是支持重入加锁的
synchronized(myLock){ (1)
// 代码
synchronized(myLock){ (2)
//代码
}
}
如果一个线程第一次执行到synchronized (1) 那里,会获取到myLock对象的monitor的锁,计数器会加1,然后第二次执行synchronized (2) 那里,会再次获取到myLock对象的monitor的锁,这个就是重入锁,然后计数器会再次加1,变成 2
这个时候如果其它线程,第一次执行到synchronized那里,会发现myLock对象的monitor锁的计数器是大于0的,意味着被别的线程加锁了,然后此时线程会进入block阻塞状态,等待获取锁。
解锁流程
synchronized(myLock){ (1)
// 代码
synchronized(myLock){ (2)
//代码
} // 解锁 计数器减1
} // 解锁 计数器减1
synchronized关键字可以同时保证原子性,可见性和有序性
原子性:原子性层面而言,有一个加锁和释放锁的机制,加锁之后,同一段代码只有自己可以执行,通过ObjectMonitor
可见性:可见性会通过加入一些内存屏障,在同步代码块对变量做的写操作,都会在释放锁的时候,全部执行flush操作,在进入同步代码块的时候,对变量的读操作,全部会强制执行refresh操作
有序性:有序性会通过各种各样的内存屏障,来解决LoadLoad,StoreStore,StoreLoad,LoadStore等等重排序,通过Acquire屏障和Release屏障,保证同步代码块内部的指令和外部的指令不能重排,Acquire就是LoadLoad和LoadStore屏障,Release就是StoreLoad和StoreStore屏障