zoukankan      html  css  js  c++  java
  • 多线程 -- 各种锁的概念

    0、前言

    JAVA 亦或是 OS 中会出现非常之多不同的锁,这些锁大多都按特性、功能、设计、属性等作为依据来进行分类,而不是具体到某一种代码实现

     

    1、公平锁 vs 非公平锁

    概念:在并发环境中,线程在获得锁的顺序/优先级是根据申请的时间顺序来安排的(FIFO),这样保证了所有的线程都有机会得到锁,不会产生操作系统概念中 “饥饿” 情况的产生,这样的锁机制称为公平锁。相反,如果在获得锁的过程中,先申请锁的线程有可能被后申请的锁抢占的情况则为非公平锁。

     

    JAVA例子:JAVA 中 ReentrantLock 就可以分别实现公平和非公平两种方式。源码解析

     

    2、可重入锁(递归锁) vs 不可重入锁

    概念:可重入锁指的是线程获得了某种锁(不妨称为 A 锁)且在其执行的过程中需要重复获得 A 锁,那么这个线程可依旧进入被 A 锁锁住的代码段。而不可重入锁则正好相反,一个线程不能够多次重复地进入一种锁。(如果你了解递归,那么可重入锁被也称递归锁从概念上看是十分好理解的)

     

    代码示例:

    下面的例子中,执行 methodA 需要获得两次 A 锁,如果 A 锁是可重入锁那么 methodB 就可以得以执行,否则(即A锁是不可重入锁)那么执行到 methodB 段就会被阻塞,需要 A锁 被执行 unlock 操作之后才能继续执行下去

    public class Demo {
      private Lock lockA;
     
      public Demo(Lock Lock) {
        this.lockA = lock;
      }
     
      public void methodA() {
        lockA.lock();
        methodB(); // 进入 B 后需要重复获得 lockA 锁
        lockA.unlock();
      }
     
      public void methodB() {
        lockA.lock();
        //dosm
        lockA.unlock();
      }
      }
    View Code

    自旋锁就是一种不可重入锁

    使用 JAVA 代码实现简单的自旋锁(CAS)

    public class SpinLock {
    
      private AtomicReference<Thread> sign =new AtomicReference<>();
    
      public void lock(){
        Thread current = Thread.currentThread();
        while(!sign .compareAndSet(null, current)){
        }
      }
    
      public void unlock (){
        Thread current = Thread.currentThread();
        sign .compareAndSet(current, null);
      }
    }
    CAS自旋锁

    CAS方式实现的乐观自旋锁能够让线程一直处于活跃状态,不会被挂起,减少了上下文切换,提高效率。

     

    JAVA例子:JAVA 中 ReentrantLock 和 Synchronized 都是可重入锁

     

    3、悲观锁 vs 乐观锁

    概念:

    悲观锁 => 有一个“悲观”的心态,既每次取数据的时候,都会认为该数据会被修改,所以必须加一把锁才安心。

    乐观锁 => 乐观的孩子,认为同一个数据不会发生并发操作的行为,所以取的时候不会加锁,只有在更新的时候,会通过例如版本号之类的来判断是否数据被修改了。

     

    JAVA例子:JAVA中的乐观锁基本是用 CAS 思想实现,例如 AtomicInteger

     

    4、共享锁 vs 排他锁 (数据库事务)

    概念:

    共享锁 => 也称读锁或S锁。如果事务对数据A加上共享锁后,则其他事务只能对A再加共享锁,不能加排它锁。获准共享锁的事务只能读数据,不能修改数据。

    排它锁 => 也称独占锁、写锁或X锁。如果事务对数据A加上排它锁后,则其他事务不能再对A加任何类型的锁。获得排它锁的事务即能读数据又能修改数据。

     

    JAVA例子:ReetrantReadWriteLock()

     

    5、分布式锁

    概念:我们上面聊的这些锁,都是在单个程序上面的不同线程之间来实现的,那么当我们的不同程序需要去竞争同一块资源的时候,这就需要分布式锁了,我们可以通过redis、zookeeper等中间件来实现分布式锁。

     

    6、偏向锁、轻量级锁、重量级锁

    简述:在 JAVA 中有个关键字 Synchronized 用以实现同步,底层在 JDK1.6 之前采用的是重量级锁,之所以说是重量级锁,是因为它要用到操作系统底层的 mutex lock 来实现锁机制,并发情况下,用这种方式的锁来实现同步,会使得在线程切换的时候需要进行上下文切换(CPU从一个进程/线程切换到另外一个进程/线程)。这种依赖底层操作系统的 mutex 相关指令实现,加锁解锁需要在用户态和内核态之间切换,性能损耗非常明显。研究人员发现,大多数对象的加锁和解锁都是在特定的线程中完成。也就是出现线程竞争锁的情况概率比较低。所以在 JDK1.6 之后 JAVA 对这个关键字进行了优化。让加锁过程有个升级的过程,分别是 无锁状态 => 偏向锁 => 轻量级锁 => 重量级锁,升级过程不可逆。具体每种锁到底如何实现,可以查看这个参考链接

     

    ---------------------------------------------

    全文引用内容参考:link1link2

    ---------------------------------------------

  • 相关阅读:
    P4363 [九省联考2018]一双木棋chess 状压DP
    P5290 [十二省联考2019] 春节十二响 贪心
    P3747 [六省联考2017]相逢是问候 欧拉公式
    P5443 [APIO2019]桥梁 操作分块+可撤销并查集
    P4146 序列终结者 FHQ_TREAP
    108. Convert Sorted Array to Binary Search Tree
    神经网络中Epoch、Iteration、Batchsize相关理解
    905. Sort Array By Parity
    100. Same Tree
    538. Convert BST to Greater Tree
  • 原文地址:https://www.cnblogs.com/qwertiLH/p/12494448.html
Copyright © 2011-2022 走看看