多线程与乐观锁和悲观锁

  • 引用

拉夫德尔的博客

  • 正文
    • 锁存在的意义
      假设在程序中有一个整型变量n,我们设计一个方法f()对n进行自增。那么当n=1时调用f()方法,返回的n应当为几?
      如果没有过并行开发经验,一定认为n为2。但是结果往往并不确定,在f()方法内,首先我们要获取n的值,然后调用自增方法,最后返回自增后的结果。所以f()方法并不是一个原子方法。当多个线程同时调用这一个方法的时候,由于cpu的时间片轮转机制,方法可能会在任何位置停止住,然后交给另外的线程使用。
      当线程t1获取n的值尚未自增时,线程的时间到了,该线程被挂起,线程t2开始运行,此时的n没有自增过依然是1,t2调用了f()方法,自增n后返回了2,并且释放了资源。此时t1重新开始执行,因为之前它已经获取了n的值为1,所以此刻它只需要继续自增n返回就好,于是又返回了一个2。
      于是我们的程序调用了两次f(),但是n却只自增了一次。
      这便是线程冲突,为了解决这个问题,我们需要对这段代码加锁,当一个线程开始执行方法的时候,使得其他线程无法获取这个方法的锁,所以只能在等待队列中等待,直至前一个线程运行完解锁。
    • 悲观锁
      总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。再比如Java里面的同步原语synchronized关键字的实现也是悲观锁。
      • 悲观锁存在的问题
        1. 在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题。
        2. 一个线程持有锁会导致其它所有需要此锁的线程挂起。
        3. 如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能风险。
    • 乐观锁
      顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。
      • 乐观锁的缺点
        当更新数据失败时不对数据进行修改返回失败的信息,并重复执行直至更新成功。当线程冲突很多的程序中效率低下。增加了cpu的负荷。

你可能感兴趣的:(多线程与乐观锁和悲观锁)