作者: 小川君 | 来源:发表于2018-10-02 01:30 被阅读0次

锁从宏观上分为悲观锁与乐观锁

乐观锁

乐观锁是一种乐观思想,即认为读多写少,遇到并发写的可能性低,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,采取在写时先读出当前版本号,然后加锁操作(比较跟上一次的版本号,如果一样则更新),如果失败则要重复读-比较-写的操作。
  java中的乐观锁基本都是通过CAS操作实现的,CAS是一种更新的原子操作,比较当前值跟传入值是否一样,一样则更新,否则失败。

悲观锁

悲观锁是就是悲观思想,即认为写多,遇到并发写的可能性高,每次去拿数据的时候都认为别人会修改,所以每次在读写数据的时候都会上锁,这样别人想读写这个数据就会block直到拿到锁。java中的悲观锁就是Synchronized

公平锁/非公平锁

公平锁和非公平锁的区别就在于,公平锁是FIFO机制,谁先来谁就在队列的前面,就能优先获得锁。非公平锁支持抢占模式,先来的不一定能得到锁(ReentrantLock)

为什么需要非公平锁,因为线程从阻塞到被唤醒是需要代价的

    java的线程是映射到操作系统原生线程之上的,如果要阻塞或唤醒一个线程就需要操作系统介入,
    需要在户态与核心态之间切换,这种切换会消耗大量的系统资源,因为用户态与内核态
    都有各自专用的内存空间,专用的寄存器等,用户态切换至内核态需要传递给许多变量、
    参数给内核,内核也需要保护好用户态在切换时的一些寄存器值、变量等,以便内核态
    调用结束后切换回用户态继续工作。

    如果线程状态切换是一个高频操作时,这将会消耗很多CPU处理时间; 
    如果对于那些需要同步的简单的代码块,获取锁挂起操作消耗的时间比用户代码执行的时间还要长,这种同步策略就会变得很吃性能

重入锁

某线程获取到锁之后,再次获取锁的时候,不会产生阻塞,而是会将锁的数量+1,线程释放锁的时候-1,直到为0锁完全释放,由下一个线程尝试获取。(ReentrantLock)

由重量级分为自旋锁、轻量锁、偏向锁、重量级锁。

自旋锁

自旋锁就是让该线程等待一段时间,不会被立即挂起,如果持有锁的线程很快就能释放锁,那么该线程就可以直接获取锁;如果执行了几个空循环之后还是不能获取锁,才会被挂起
使用自旋锁后,线程被挂起的几率相对减少,线程执行的连贯性相对加强。因此,对于那些锁竞争不是很激烈,锁占用时间很短的并发线程,具有一定的积极意义;但对于锁竞争激烈,单线程锁占用很长时间的并发程序,自旋锁在自旋等待后,往往毅然无法获得对应的锁,不仅仅白白浪费了CPU时间,最终还是免不了被挂起的操作 ,反而浪费了系统的资源

偏向锁

在单线程(非并发情况,即没有竞争锁的时候)访问同步代码块的时候,可以忽略同步锁机制,来提升性能

轻量级锁

具体表现为自旋锁,如果自旋结束还是没有获取锁,那么升级成重量级锁,线程阻塞,等待被唤醒

重量级锁

重量级锁在操作过程中,线程可能会被操作系统层面挂起,如果是这样,线程间的切换和调用成本就会大大提高

相关文章

网友评论

      本文标题:

      本文链接:https://www.haomeiwen.com/subject/oilwoftx.html