JAVA基础-并发-ReentrantLock

本文详细介绍了ReentrantLock的工作原理及其与synchronized关键字的区别。ReentrantLock基于AQS框架实现,支持公平锁与非公平锁,能够灵活控制锁的获取与释放行为。

本文介绍 ReentrantLock 及其实现原理,ReentrantLock和同步工具类的实现基础都是AQS

ReentrantLock 与 synchronize 的区别
  • API层面,synchronize是jvm底层实现,ReentrantLock是java代码实现
  • synchronize是关键字,ReentrantLock是java类
  • 等待可中断,synchronized不可以中断,ReentrantLock可以设置超时等待避免死索
  • synchronized的锁是非公平锁,ReentrantLock默认也是非公平锁,但可以设置成公平锁
  • ReentrantLock可以同时绑定多个Condition对象,只需多次调用newCondition方法即可。
什么是AQS
  • AQS即是AbstractQueuedSynchronizer,一个用来构建锁和同步工具的框架,包括常用的ReentrantLock、CountDownLatch、Semaphore等。
  • AQS没有锁之类的概念,它有个state变量,是个int类型,在不同场合有着不同含义。本文研究的是锁,为了好理解,姑且先把state当成锁。
  • AQS围绕state提供两种基本操作“获取”和“释放”,有条双向队列存放阻塞的等待线程,并提供一系列判断和处理方法,简单说几点:
    • state是独占的,还是共享的;
    • state被获取后,其他线程需要等待;
    • state被释放后,唤醒等待线程;
    • 线程等不及时,如何退出等待。
  • 至于线程是否可以获得state,如何释放state,就不是AQS关心的了,要由子类具体实现。

ReentrantLock实现了独占功能

AbstractQueuedSynchronizer Node 的一些属性
属 性定 义举例使用地方
Node SHARED = new Node()表示Node处于共享模式在获取锁失败后会将线程放入队列中,这个时候要指定Node的模式
Node EXCLUSIVE = null表示Node处于独占模式了在获取锁失败后会将线程放入队列中,这个时候要指定Node的模式
ReentrantLock的使用

直接上代码,锁的使用简单来说就是加锁,执行自己的方法,释放锁

Lock lock = new ReentranLock();
lock.lock();
try{
    //do something
}finally{
    lock.unlock();
}

ReentrantLock 实现了公平锁和非公平锁,通过在构造方法中传入true或者false,它们两主要是在释放锁后对队列的唤醒操作不一样,公平锁是从队列头中唤醒,而非公平锁是所有都唤醒,然后来争抢资源。

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}
lock() 方法

公平锁:

      /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            //获取当前的锁状态,如果为0则继续
            int c = getState();
            if (c == 0) {
                // hasQueuedPredecessors 因为是公平锁所以需要判断当前队列是否有在等待的,
                //如果队列中并等待,则调用compareAndSetState 加锁并设置当前线程为自己
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //判断获取锁的是不是当前线程本身,线程可以不停地lock来增加state的值,对应地需要unlock来解锁,直到state为零。
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

非公平锁:与公平锁主要的差异是公平锁会调用hasQueuedPredecessors

   /**
         * 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;
                }
            }
            //判断获取锁的是不是当前线程本身,线程可以不停地lock来增加state的值,对应地需要unlock来解锁,直到state为零。
            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;
        }
线程进入等待队列

当获取锁失败时,线程会进入队列中

 public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
   /**
     * Creates and enqueues node for current thread and given mode.
     *
     * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
     * @return the new node
     */
    private Node addWaiter(Node mode) {
        // 创建节点,mode 是 Node.EXCLUSIVE 独占模式
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }

enq是个死循环,保证Node一定能插入队列。注意到,当队列为空时,会先为头节点创建一个空的Node,因为头节点代表获取了锁的线程,现在还没有,所以先空着。

private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

执行完addWaiter 会执行 acquireQueued,

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
        // 线程唤醒后尝试获取锁的过程。如果前一个节点正好是head,表示自己排在第一位,可以马上调用tryAcquire尝试。如果获取成功就简单了,直接修改自己为head。这步是实现公平锁的核心,保证释放锁时,由下个排队线程获取锁。
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            // 线程获取锁失败的处理。这个时候,线程可能等着下一次获取,也可能不想要了,
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
释放锁 unlock()

里面调用的是release

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        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;
    }

寻找下个待唤醒的线程是从队列尾向前查询的,找到线程后调用LockSupport的unpark方法唤醒线程。被唤醒的线程重新执行acquireQueued里的循环,上面有提到acquireQueued

/**
 * Wakes up node's successor, if one exists.
 *
 * @param node the node
 */
private void unparkSuccessor(Node node) {
    /*
     * If status is negative (i.e., possibly needing signal) try
     * to clear in anticipation of signalling.  It is OK if this
     * fails or if status is changed by waiting thread.
     */
    int ws = node.waitStatus;
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);

    /*
     * Thread to unpark is held in successor, which is normally
     * just the next node.  But if cancelled or apparently null,
     * traverse backwards from tail to find the actual
     * non-cancelled successor.
     */
    Node s = node.next;
    if (s == null || s.waitStatus > 0) {
        s = null;
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    if (s != null)
        LockSupport.unpark(s.thread);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值