乐观锁与悲观锁

乐观锁与悲观锁的区别?

  • 悲观锁:共享资源每次只给一个线程使用,其他线程阻塞,用完之后再把资源让给其他资源。但是读写锁可以多个线程同时读。
  • 乐观锁:共享资源每次可以给多个线程使用,只是再提交修改的时候去验证资源是否被其他线程所修改。

使用场景

  • 悲观锁:适合写多读少的情况。
  • 乐观锁:适合读多写少的情况。

缺陷

  • 悲观锁:高并发下,激烈的锁竞争会造成线程阻塞,大量的线程会导致系统上下文切换频繁,增加系统的性能开销;还有可能导致死锁。
  • 乐观锁:高并发下,相比悲观锁来所,不存在锁竞争造成线程阻塞,也不会有死锁问题,性能往往更好。但是再写冲突高的场景下可能会导致ABA问题,性能忽高忽低等

CAS了解吗?原理是什么?

乐观锁一般会使用版本号机制或者CAS算法实现,CAS算法使用会多一些。值得一提的是,这里的版本号机制和CAS算法中的ABA问题的解决方案是同一个东西。

CAS算法的思想很简单,就是用一个预期值和要更新的变量值进行比较,两个值相等才会进行更新。这里的比较和修改两个步骤再CPU硬件层面是一条原子指令,是不可拆分的,这也是CAS算法的基础。

比如ConcurrentHashMap采用的就是CAS和synchronized来保证并发安全的。
原子类也是

JMM(Java memory model)

并发编程的三个重要特性

  • 原子性:一个或者多个操作要么全部执行且不中断,要么全部不执行。
  • 可见性:当一个线程修改了共享变量,其他线程能够立刻看到这个修改
  • 有序性:编译器优化和指令重排在多线程情况下,可能导致意外的结果。因此保证有序性可以禁止指令进行重排序优化。

实现手段

  • 原子性:一般都是通过加锁去实现,当然也可以使用CAS实现。
  • 可见性:
    • 使用volatile关键字
    • synchronizedlock同步块
    • final关键字再构造函数中有正确初始化(没有this逃逸)
  • 有序性:
    • volatile
    • synchronizedlock同步块
    • happens-before原则:在多线程环境中,当两个操作之间存在 happens-before 关系时,前一个操作的修改对后一个操作是可见的。