zoukankan      html  css  js  c++  java
  • Java多线程系列--“JUC锁”05之 非公平锁

    概要

    前面两章分析了"公平锁的获取和释放机制",这一章开始对“非公平锁”的获取锁/释放锁的过程进行分析。内容包括:
    参考代码
    获取非公平锁(基于JDK1.7.0_40)
    释放非公平锁(基于JDK1.7.0_40)
    关于锁的数据结构请参考"Java多线程系列--“JUC锁”03之 公平锁(一) ",锁的使用示例请参考“Java多线程系列--“JUC锁”02之 互斥锁ReentrantLock”。

    转载请注明出处:http://www.cnblogs.com/skywang12345/p/3496651.html

    参考代码

    下面给出Java1.7.0_40版本中,ReentrantLock和AQS的源码,仅供参考!

    ReentranLock.java

     View Code

    AQS(AbstractQueuedSynchronizer.java)

     View Code

    获取非公平锁(基于JDK1.7.0_40)

    非公平锁和公平锁在获取锁的方法上,流程是一样的;它们的区别主要表现在“尝试获取锁的机制不同”。简单点说,“公平锁”在每次尝试获取锁时,都是采用公平策略(根据等待队列依次排序等待);而“非公平锁”在每次尝试获取锁时,都是采用的非公平策略(无视等待队列,直接尝试获取锁,如果锁是空闲的,即可获取状态,则获取锁)。
    在前面的“Java多线程系列--“JUC锁”03之 公平锁(一)”中,已经详细介绍了获取公平锁的流程和机制;下面,通过代码分析以下获取非公平锁的流程。

    1. lock()

    lock()在ReentrantLock.java的NonfairSync类中实现,它的源码如下:

    final void lock() {
        if (compareAndSetState(0, 1))
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }

    说明
    lock()会先通过compareAndSet(0, 1)来判断“锁”是不是空闲状态。是的话,“当前线程”直接获取“锁”;否则的话,调用acquire(1)获取锁。
    (01) compareAndSetState()是CAS函数,它的作用是比较并设置当前锁的状态。若锁的状态值为0,则设置锁的状态值为1。
    (02) setExclusiveOwnerThread(Thread.currentThread())的作用是,设置“当前线程”为“锁”的持有者。

    “公平锁”和“非公平锁”关于lock()的对比

    公平锁   -- 公平锁的lock()函数,会直接调用acquire(1)。
    非公平锁 -- 非公平锁会先判断当前锁的状态是不是空闲,是的话,就不排队,而是直接获取锁。

    2. acquire()

    acquire()在AQS中实现的,它的源码如下:

    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

    (01) “当前线程”首先通过tryAcquire()尝试获取锁。获取成功的话,直接返回;尝试失败的话,进入到等待队列依次排序,然后获取锁。
    (02) “当前线程”尝试失败的情况下,会先通过addWaiter(Node.EXCLUSIVE)来将“当前线程”加入到"CLH队列(非阻塞的FIFO队列)"末尾。
    (03) 然后,调用acquireQueued()获取锁。在acquireQueued()中,当前线程会等待它在“CLH队列”中前面的所有线程执行并释放锁之后,才能获取锁并返回。如果“当前线程”在休眠等待过程中被中断过,则调用selfInterrupt()来自己产生一个中断。

    “公平锁”和“非公平锁”关于acquire()的对比

    公平锁和非公平锁,只有tryAcquire()函数的实现不同;即它们尝试获取锁的机制不同。这就是我们所说的“它们获取锁策略的不同所在之处”!
    在“Java多线程系列--“JUC锁”03之 公平锁(一)”中,已经详细介绍了acquire()涉及到的各个函数。这里仅对它们有差异的函数tryAcquire()进行说明。

    非公平锁的tryAcquire()在ReentrantLock.java的NonfairSync类中实现,源码如下:

    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }

    nonfairTryAcquire()在ReentrantLock.java的Sync类中实现,源码如下:

    复制代码
    final boolean nonfairTryAcquire(int acquires) {
        // 获取“当前线程”
        final Thread current = Thread.currentThread();
        // 获取“锁”的状态
        int c = getState();
        // c=0意味着“锁没有被任何线程锁拥有”
        if (c == 0) {
            // 若“锁没有被任何线程锁拥有”,则通过CAS函数设置“锁”的状态为acquires。
            // 同时,设置“当前线程”为锁的持有者。
            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;
    }
    复制代码

    说明
    根据代码,我们可以分析出,tryAcquire()的作用就是尝试去获取锁。
    (01) 如果“锁”没有被任何线程拥有,则通过CAS函数设置“锁”的状态为acquires,同时,设置“当前线程”为锁的持有者,然后返回true。
    (02) 如果“锁”的持有者已经是当前线程,则将更新锁的状态即可。
    (03) 如果不术语上面的两种情况,则认为尝试失败。

    “公平锁”和“非公平锁”关于tryAcquire()的对比

    公平锁和非公平锁,它们尝试获取锁的方式不同。
    公平锁在尝试获取锁时,即使“锁”没有被任何线程锁持有,它也会判断自己是不是CLH等待队列的表头;是的话,才获取锁。
    而非公平锁在尝试获取锁时,如果“锁”没有被任何线程持有,则不管它在CLH队列的何处,它都直接获取锁。

    释放非公平锁(基于JDK1.7.0_40)

    非公平锁和公平锁在释放锁的方法和策略上是一样的。
    而在前面的“Java多线程系列--“JUC锁”04之 公平锁(二) ”中,已经对“释放公平锁”进行了介绍;这里就不再重复的进行说明。

    总结
    公平锁和非公平锁的区别,是在获取锁的机制上的区别。表现在,在尝试获取锁时 —— 公平锁,只有在当前线程是CLH等待队列的表头时,才获取锁;而非公平锁,只要当前锁处于空闲状态,则直接获取锁,而不管CLH等待队列中的顺序。
    只有当非公平锁尝试获取锁失败的时候,它才会像公平锁一样,进入CLH等待队列排序等待。

  • 相关阅读:
    c 中有关打印*,字符的题目集
    c 求两个整数的最大公约数和最小公倍数
    cygwin中vim的使用方法
    window下如何搭建linux环境
    ORA-00923: 未找到要求的 FROM 关键字
    c# datagridviewcomboboxcell值无效的解决办法
    sql substring charindex
    C# 中datagridview行里面有三个cheeckbox,要控制成三选一。
    程序中使用事务来管理sql语句的执行,执行失败时,可以达到回滚的要求。
    WinForm界面中快捷键设置
  • 原文地址:https://www.cnblogs.com/duanxz/p/6063695.html
Copyright © 2011-2022 走看看