04-悲观锁和乐观锁的区别和应用场景

讲悲观锁和乐观锁之前,顺便复习一下同步锁和死锁

1、同步锁

  • 同步锁是为了保证每个线程都能正常执行原子不可更改操作,同步监听"对象/同步锁/同步监听器/互斥锁"的一个标记锁
  • 每个Java对象有且只有一个同步锁,在任何时刻,最多只允许一个线程拥有这把锁,当消费者线程试图执行以带有synchronized(this)标记的代码块的时候,消费者线程必须先取得this关键字引用的Stack对象的锁

2、死锁

  • 线程死锁是指由于两个或者多个线程互相持有对方所需资源,导致这些线程处于等待状态,无法前往执行;当线程进入对象的synchronized代码块,便占有了资源,直到它退出该代码块或者调用wait方法,才会释放资源,如果线程都不主动释放所占有的资源,将产生死锁

  • 死锁的产生是必须要满足一些特定条件的

    • 互斥条件

      • 进程对于所分配的资源具有排它性,即一个资源只能被一个进程占用,直到被该进程释放
    • 请求和保持条件

      • 一个进程因请求被占用资源而发生阻塞时,对已获得的资源保持不放
    • 不剥夺条件

      • 任何一个资源在没被该进程释放之前,任何其他进程都无法对他剥夺占用
    • 循环等待条件

      • 当发生死锁的时候,所等待的线程必定会形成一个环路(有线程获取锁的顺序是 A -> B,有的线程获取锁的顺序是 B -> A,形成一个环路),造成永久阻塞

3、悲观锁

  • 悲观锁(Pessimistic Lock)总是假设最坏的情况每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿到这个数据就会阻塞直到它拿到锁
  • 应用场景
    • 传统的关系数据库里用到了很多这种锁机制,比如按使用性质划分的读锁,写锁和按作用范围划分的行锁,表锁。对于可能冲突的并发操作,以串行的方式取代并发执行,因而它也是一种悲观并发控制

4、乐观锁

  • 乐观锁(Optimistic Lock)总是假设最好的情况每次去拿数据的时候都认为别人不会修改,所以不会上锁;但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制CAS算法实现版本号机制
  • 实现方式
    • 在数据表中假如一个数据版本号version字段,表示数据被修改的次数,当数据被修改的时候,version值会加一;当线程A要重新更新数据值的时候,在读取数据的时候也会读取version值,在提交更新的时候,若刚才读取到的version值与当前数据库中的version值相等才更新,否则重新更新操作,直到更新成功
  • CAS算法
    • CAS(Compare And Swap)比较并交换算法,是一种有名的无锁算法,在不适用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量同步,所以也叫非阻塞同步
    • CAS算法设计到三个操作数
      • ①、要更新的变量v
      • ②、预期的值E
      • ③、新值N
    • 仅当v值等于E值的时候,才会将v的值设置为N,否则说明都不做,最后CAS返回当前v的值,CAS算法需要额外给出一个期望值,也就是个人认为现在变量应该是这样子,如果变量不是个人所期望的样子,就说明已经被别人修改过,就重新读取,再次尝试修改即可

5、乐观锁和悲观锁的应用场景

  • 乐观锁使用于写比较少(即读多)的场景,即冲突很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但是如果是写比较多的情况,一般会经常发生冲突,这就会导致上层应用会不断的进行充实,这样反倒是降低了性能,所以一般多写的场景下用悲观锁比较合适
posted @ 2022-08-02 13:54  OnlyOnYourself-Lzw  阅读(166)  评论(0编辑  收藏  举报