一,synchronized关键字
synchronized是java的一个关键字,可以修饰方法和代码块,修饰方法会有一个ACC_SYNCHRONIZED标志,修饰代码快会有monitorEnter和monitorExit指令,这是在jvm之上的,修饰方法的也是会在方法开始前拿到monitor,所有synchronized都是通过MonitorObject来实现锁的,而MonitorObject会有一些队列,表示等待队列和和等待加锁队列,大家可以去看下源码,而每个synchronized都会对应一个锁对象,来记录锁的一些信息,jdk6后面的优化加入了锁消除,偏向锁,乐观锁和重量级锁,所消除就是如果jvm分析到没有锁竞争,或者是对象没有逃逸,那么就可以进行锁消除,如果锁的竞争很少,只有一个线程拿到锁,那么会记录偏向锁,如果锁的竞争不多,并且锁的持有时间不长,那么会升级到乐观锁,也就是自旋锁,如果竞争多,并且时间长,那么就是最后的重量级锁。
这里不分析锁消除,偏向锁和自旋锁,因为他们都是jvm自己的优化,原理也比较简单,这里分析重量级锁。在操作系统中,操作系统提供一些原语,也就是原子操作,包括CAS,TS(Test and set) ,P ,V,这里CAS后面会说,TS是操作系统互斥锁的实现,而PV操作表示申请和释放资源,也可以用来实现互斥锁或者是共享锁,P表示如果资源<=0则循环等待,如果>0则进行-1操作,这个过程是原子的,也就是说,synchronized依赖于jvm的MonitorObject实现,MonitorObject依赖于操作系统底层的互斥锁原语实现
二,volatile关键字
需要说明的是volatile并不能保证线程安全,只能保证线程可见性,也就是说线程A的修改,能够保证其他线程及时看到修改,并不能保证并发的安全性,那么他的原理是什么呢。
volatile关键字编译成机器码后会看到这个关键字伴随的是一个#LOCK 指令,这个指令是处理器提供的一个信号,处理器收到LOCK后会对相应缓存进行锁定,一般是对整个缓存行进行锁定,如果缓存未对齐,或者是对应多个缓存行,则会进行总线锁定,而多个处理器核心之间通过缓存一致性协议来保证缓存的一致性,也就是缓存有四种状态
如果一个核心修改了锁定的缓存行,则会立刻刷新到主存中,嗅探机制会将其他核心缓存行的数据设置为无效,那么核心将会从主存重新读取数据,这样保证的数据的可见性,而也正是因为有缓存锁的机制,volatile没有屏障,但是却有着屏障的功能,保证了有序性
详细的可以参考JVM LOCK#指令_dw147258dw的专栏-CSDN博客
也就是说volatile的成本就是缓存失效,修改数据后从主存读取数据
三,Lock接口



