引言

java中提供了synchronized关键字和java.util.concurrent.locks包下的相关类来进行线程同步

synchronized关键字

在1.6之前synchronized是一个重量级锁,此同步方式同步成本非常高,因为阻塞线程会引起用户态和内核态之间切换。此后jvm对此同步方式进行了许多优化(偏向锁、轻量级锁)可以放心使用,而且因为是java关键字所以也可以享受以后的优化并且不需要更改代码

修饰锁对象
静态方法当前类对象(class)
实例方法当前实例(this)
修饰代码块指定变量

synchronized锁膨胀过程 [偏向锁] -> [轻量级锁] -> [重量级锁]

  • 偏向锁
    偏向锁会在第一次进行同步访问时的将访问线程的线程ID使用CAS记录到互斥变量的Mark word
    以后每次访问则检查访问线程的ID是否等于Mark word中记录的线程ID,如果等于则可以无锁同步。如果线程ID不相同则说明有其他线程竞争,此时偏向锁会膨胀成轻量级锁
    适用场景:自始至终仅有同一线程访问
    优点:可以实现无锁同步
    缺点:如果很明显有其他线程竞争则很快将膨胀成轻量级锁
  • 轻量级锁
    加锁:为访问线程创建Lock record,将锁对象的Mark word复制到Lock record称为Displaced mark word,尝试使用CASMark word指向Displaced mark word,如果成功则获取锁成功,否则循环重试
    解锁:使用Displaced mark word替换原对象的Mark word
    轻量级锁会在重试次数过多时膨胀成重量级锁
    适用场景:线程竞争少,非耗时操作
    优点:可以避免线程阻塞造成的造成的用户态和内核态切换成本
    缺点:如果进行耗时操作则会循环获取锁占用CPU资源
  • 重量级锁
    重量级锁会在利用操作系统底层创建互斥量实现,同步时会造成内核态切换
    适用场景:线程竞争激烈,耗时操作
    优点:线程直接阻塞无CPU资源占用
    缺点:线程阻塞,阻塞和唤醒线程耗时可能会超过代码本身执行时间,性能可能低于轻量级锁

ReentrantLock锁

ReentrantLock分为公平锁和非公平锁,内部使用CAS方式实现具体原理类似于轻量级锁

对比

  • synchronized
    1. java关键字,易用
    2. 无需改代码享受jdk优化
  • ReentrantLock
    1. java类,加锁解锁都是实例方法,可以更灵活的控制
    2. 提供Condition可以灵活的控制要唤醒的线程
    3. 可以实现公平锁
    4. 可以中断等待