并发编程实战4-自旋锁,死锁,以及锁重入详解

  • 锁重入:也叫做递归锁
    • 某个线程获得一个已经由它自己持有的锁对象,那么这个请求就会成功,即重入
    • 重入是对本线程来说,即本线程多资源可以多次加锁进入,而不会出现阻塞
    • 在JAVA环境下 ReentrantLock 和synchronized 都是可重入锁
    • 场景:比如数据库中,用户名和密码保存在本地txt文件中,则登录验证方法和更新密码方法都应该被加synchronized,那么当更新密码的时候需要验证密码的合法性,所以需要调用验证方法,相当于二次入锁,此时是可以调用的。
  • 自旋锁
    • 当一个线程访问的资源被其他线程占用时,此线程并不会等待,而是一直尝试获取锁,即仍然占用CPU,直到获取锁为止。
  • 死锁
    • 资源互斥导致互相占用不放

  • volatile变量原理与使用链接:IBM-正确使用 Volatile 变量
    • Volatile称之为轻量级锁,被volatile修饰的变量,在线程之间是可见的(处理器内部lock指令标记)。
    • 可见:一个线程修改了这个变量的值,在另外一个线程中能够立即读到这个修改后的值。
    • volatile 变量可以被看作是一种 “程度较轻的 synchronized”;与 synchronized 块相比,volatile 变量所需的编码较少,并且运行时开销也较少,但是它所能实现的功能也仅是 synchronized 的一部分。Volatile 变量具有synchronized 的可见性特性,但是不具备原子特性。
    • 要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件:
      • 对变量的写操作不依赖于当前值。
      • 该变量没有包含在具有其他变量的不变式中。
        第一个条件的限制使 volatile 变量不能用作线程安全计数器。虽然增量操作(x++)看上去类似一个单独操作,实际上它是一个由读取-修改-写入操作序列组成的组合操作,必须以原子方式执行,而 volatile 不能提供必须的原子特性。实现正确的操作需要使 x 的值在操作期间保持不变,而 volatile 变量无法实现这点。(然而,如果将值调整为只从单个线程写入,那么可以忽略第一个条件。)

  • JDK1.5提供的原子类原理与应用
    Atomic一词跟原子有点关系,后者曾被人认为是最小物质的单位。计算机中的Atomic是指不能分割成若干部分的意思。如果一段代码被认为是Atomic,则表示这段代码在执行过程中,是不能被中断的。通常来说,原子指令由硬件提供,供软件来实现原子方法(某个线程进入该方法后,就不会被中断,直到其执行完成)
    在x86 平台上,CPU提供了在指令执行期间对总线加锁的手段。CPU芯片上有一条引线#HLOCK pin,如果汇编语言的程序中在一条指令前面加上前缀”LOCK”,经过汇编以后的机器代码就使CPU在执行这条指令的时候把#HLOCK pin的电位拉低,持续到这条指令结束时放开,从而把总线锁住,这样同一总线上别的CPU就暂时不能通过总线访问内存了,保证了这条指令在多处理器环境中的原子性。

    • 原子变量的原子操作也是用到了锁,只不过这个是硬件指令级别的锁,比我们软件实现的锁高效很多,更重要的是如果出现了冲突,只是不停的循环重试,而不会切换线程。如:do{ }while 循环调用一个本地方法。
  • Lock锁的认识与使用

    • lock的使用类似如下:
      lock使用
    • 使用方式类似于synchronized,具有相同的互斥性和内存可见性,但是更加灵活,加锁和放锁可以由使用者自己确定
    • 优势:
      • synchronized锁可能出现异常而导致中断,无法释放锁,但是通过使用lock,可以直接将lock放在异常finally中,强制释放锁。
        • 可以方便的实行公平性
  • AbstractQueuedSynchronizer(AQS)详解

    • AQS是一个用来构建锁和同步器的框架,它在内部定义了一个int state变量,用来表示同步状态.在LOCK包中的相关锁(常用的有ReentrantLock、 ReadWriteLock)都是基于AQS来构建.然而这些锁都没有直接来继承AQS,而是定义了一个Sync类去继承AQS.那么为什么要这样呢?because:锁面向的是使用用户,而同步器面向的则是线程控制,那么在锁的实现中聚合同步器而不是直接继承AQS就可以很好的隔离二者所关注的事情。

    • 即AQS用来记录线程状态,并不管理线程,所以是内部定义,而不是直接继承

    • java.util.concurrent中的同步器类:

      • RentrantLock

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        /** 源码
        * Performs non-fair tryLock. tryAcquire is implemented in
        * subclasses, but both need nonfair try for trylock method.
        */
        final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
        if (compareAndSetState(0, acquires)) {
        setExclusiveOwnerThread(current);
        return true;
        }
        }
        else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) // overflow
        throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
        }
        return false;
        }

        protected final boolean tryRelease(int releases) {
        int c = getState() - releases;
        if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
        boolean free = false;
        if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
        }
      • ReentrantReadWriteLock

  • 公平锁和非公平锁(是否考虑队列问题)

    • 公平锁(Fair):加锁前检查是否有排队等待的线程,优先排队等待的线程,先来先得
    • 非公平锁(Nonfair):加锁时不考虑排队等待问题,直接尝试获取锁,获取不到自动到队尾等待
      非公平锁性能比公平锁高5~10倍,因为公平锁需要在多核的情况下维护一个队列,Java中的ReentrantLock 默认的lock()方法采用的是非公平锁。
Donate comment here