volatile

volatile 是一种轻量级的同步机制,用来保证变量的可见性与禁止指令重排,但不能保证原子性。适用于状态标志这类场景,不适用于复杂的共享数据更新。

用来解决多线程环境下变量的可见性问题和指令重排序问题。

实现原理:JVM在编译的时候加入内存屏障禁止指令重排序。
对于被volatile修饰的变量,要从主内存去读取,写完会刷新到主内存中

应用场景:
单一情况:boolean 状态标志
DCL双端锁(单例模式)
非原子性操作(volatile不保证原子性

面试: 内存屏障:排序栅栏,禁止重排序
粗分: 读屏障, 写屏障
细分:读读,写写,读写,写读

synchronized

synchronize是Java中的关键字,用来保证多个线程操作代码区活访问共享资源时的安全性。 可以修饰方法(实例方法或静态方法)和代码块。

synchronized的核心是通过“对象锁”实现的,底层依赖JVM的monitorenter和monitorexit,以及操作系统的互斥原语。

具体逻辑:

  1. 锁的载体:被synchronized修饰的代码必须关联一个锁对象(修饰实例对象时,锁对象时this;修饰静态方法时,锁对象是类的Class对象; 修饰代码块则是括号中的对象)
  2. 互斥逻辑:
    1. 执行到synchronized修饰的代码块时,线程尝试获取锁对象的monitor(锁监视器)
    2. 获取成功则进入临界区执行代码,同时将monitor的“持有计数”+1
    3. 获取失败则该线程阻塞并进入等待队列直到锁被释放
    4. 代码执行完毕释放锁。将monitor的持有计数-1。若计数为0,则唤醒等待队列中的线程重新竞争锁
  3. 可见性:释放锁时,JVM强制将线程工作内存中的数据刷新到主内存中;获取锁时,会清空线程工作内存,从主内存中重新加载数据。

对比ReentantLock,都是可重入锁,都保证原子性,可见性
synchronized是关键字,通过monitor和操作系统互斥原语实现的。是公平锁,加锁释放锁由JVM控制。
ReentantLock是基于AQS和CAS开发的锁工具,默认是公平锁(可设置)。需要手动调用lock()unlock()方法

经过优化,在JDK1.6之后,synchronize的性能已经和ReentantLock相当,简单场景下使用synchronized代码更简洁,不易出错。