关于并发编程,这些你必须得知道

202 阅读3分钟

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的区别

  1. 底层实现

    • synchronized是jdk的关键字,底层是通过monitor指令来实现锁的,对象只有在同步代码块中才可以调用wait/notify等一系列锁操作
    • reentranlock从jdk1.5开始提供的API层面的锁,是轻量级的乐观锁,底层通过cas和aqs实现
  2. 是否可释放

    • synchronized不需要手动是释放锁,当同步代码块执行完步,系统会自动释放锁。
    • reentranlock需要手动释放,一般lock/unlock都是配合try/finally使用,更加灵活
  3. 是否可中断

    • synchronized一般不可以中断,除非同步代码块执行异常,或者执行完成
    • reentranlock可以中断,可以通过trylock(time,unit);超时中断,以及lockInterruptibly方法,调用interrupt指令实现中断
  4. 是否可添加condition条件

    • synchronized不可以添加condition条件
    • reentranlock可以添加,通过condition条件精准唤醒线程,而不是像synchronized一样,通过notify/notifyAll一样要么随机唤醒一个线程,要么全部环境
  5. 是否公平

    • synchronized是非公平锁
    • reentranlock可以是公平锁,也可以是非公平锁,通过构造方法new ReentrantLock时传入boolean值进行选择,为空默认false非公平锁,true为公平锁。
  6. 锁的对象

    • synchronized锁得是对象,通过对象头来判断当前对象是已经获得锁还是要去争抢锁
    • reentranlock锁的是线程,通过线程和int类型的state来判断是已经获得锁还是要去争抢锁。