1.volatile的作用,以及实现原理
volatile主要是解决可见性和有序性的问题。
通过嗅探机制,处理器嗅探总线,在MESI协议基础上对数据状态进行操作从而保证可见性。 当工作内存执行写操作的时候,一个线程对变量的修改会被强制刷到主内存中,从而其他工作内存对该变量的缓存会失效,使得其他工作内存不得不去主内存中去拿最新值,从而保证可见性。
通过1.内存屏障禁止指令重排序 2.happens-before 3.as-if-serial 三种方式保证程序执行的有序性。 内存屏障分为: 硬件层面和JMM层面
硬件层面:
读屏障:在读指令前插入内存屏障,使得所有读操作的数据都失效,强制去主内存去拿数据
写屏障:在写指令后插入内存屏障,使得所有写操作都直接往主内存中更新,不经过高速缓存这一层
全屏障:群屏障兼具读屏障和写屏障的功能
lock前缀:lock不是内存屏障,而是一种锁,通过锁住内存子系统来保证指令执行顺序,甚至支持跨cpu实现。
JMM层面
loadload屏障:loadload后的语句,一定会等前面的语句执行完,才会执行
storestore屏障:storestore后的语句执行前,必须要保证前面的语句执行完毕,并且前面的执行结果对其他线程可见。
loadstore屏障:loadstore后的写指令执行前,loadstore之前的所有读指令必须读取完毕
storeload屏障:storeload后的读操作执行前,storeload前的写指令必须执行完毕,并且可见
volatile实现过程中可能带来的问题:
线程嗅探总线机制,通过大量的cas操作,消耗大量的cpu资源,导致总线风暴
2.synchronized和reentranlock的区别
-
底层实现
- synchronized是jdk的关键字,底层是通过monitor指令来实现锁的,对象只有在同步代码块中才可以调用wait/notify等一系列锁操作
- reentranlock从jdk1.5开始提供的API层面的锁,是轻量级的乐观锁,底层通过cas和aqs实现
-
是否可释放
- synchronized不需要手动是释放锁,当同步代码块执行完步,系统会自动释放锁。
- reentranlock需要手动释放,一般lock/unlock都是配合try/finally使用,更加灵活
-
是否可中断
- synchronized一般不可以中断,除非同步代码块执行异常,或者执行完成
- reentranlock可以中断,可以通过trylock(time,unit);超时中断,以及lockInterruptibly方法,调用interrupt指令实现中断
-
是否可添加condition条件
- synchronized不可以添加condition条件
- reentranlock可以添加,通过condition条件精准唤醒线程,而不是像synchronized一样,通过notify/notifyAll一样要么随机唤醒一个线程,要么全部环境
-
是否公平
- synchronized是非公平锁
- reentranlock可以是公平锁,也可以是非公平锁,通过构造方法new ReentrantLock时传入boolean值进行选择,为空默认false非公平锁,true为公平锁。
-
锁的对象
- synchronized锁得是对象,通过对象头来判断当前对象是已经获得锁还是要去争抢锁
- reentranlock锁的是线程,通过线程和int类型的state来判断是已经获得锁还是要去争抢锁。