Collin Nam


ReentrantLock 中Lock、tryLock和lockInterruptibly的区别

Frank 2019-04-26 396浏览 0条评论
首页/ 正文
分享到: / / / /

​LOCK

调用后一直阻塞到获得锁

public void lock() 
1. 获取锁。 
2. 如果该锁没有被另一个线程保持,则获取该锁并立即返回,将锁的保持计数设置为 1。 
3. 如果当前线程已经保持该锁,则将保持计数加 1,并且该方法立即返回。 
4. 如果该锁被另一个线程保持,则出于线程调度的目的,禁用当前线程,并且在获得锁之前,该线程将一直处于休眠状态,此时锁保持计数被设置为 1。

我们分析一下源码能看到,lock获取锁过程中,忽略了中断,在成功获取锁之后,再根据中断标识处理中断,即selfInterrupt中断自己。 acquire操作源码如下:

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

acquireQueued,在for循环中无条件重试获取锁,直到成功获取锁,同时返回线程中断状态。该方法通过for循正常返回时,必定是成功获取到了锁。

 */
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            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);
    }
}

LockInterruptibly

调用后一直阻塞到获得锁 但是接受中断信号(sleep,interrupt)

public void lockInterruptibly() throws InterruptedException 
1. 如果当前线程未被中断,则获取锁。 
2. 如果该锁没有被另一个线程保持,则获取该锁并立即返回,将锁的保持计数设置为 1。 
3. 如果当前线程已经保持此锁,则将保持计数加 1,并且该方法立即返回。 
4. 如果锁被另一个线程保持,则出于线程调度目的,禁用当前线程,并且在发生以下两种情况之一以前,该线程将一直处于休眠状态:锁由当前线程获得;或者其他某个线程中断当前线程。 
5. 如果当前线程获得该锁,则将锁保持计数设置为 1。 
如果当前线程: 
在进入此方法时已经设置了该线程的中断状态;或者 
在等待获取锁的同时被中断。 
则抛出 InterruptedException,并且清除当前线程的已中断状态。 
6. 在此实现中,因为此方法是一个显式中断点,所以要优先考虑响应中断,而不是响应锁的普通获取或重入获取。 
7. 指定者: 
接口 Lock 中的 lockInterruptibly 
8. 抛出: 
InterruptedException 如果当前线程已中断。

我们分析Lock接口中LockInterruptibly方法的实现,可以看到当acquireInterruptibly收到中断请求interrupted时将抛出异常,由上层调用者处理异常。

public final void acquireInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    if (!tryAcquire(arg))
        doAcquireInterruptibly(arg);
}
tryLock

尝试是否能获得锁 如果不能获得立即返回。如果锁未被另一个线程保持,则获取锁。

public boolean tryLock() 
1. 仅在调用时锁未被另一个线程保持的情况下,才获取该锁。 
2. 如果该锁没有被另一个线程保持,并且立即返回 true 值,则将锁的保持计数设置为 1。 
3. 即使已将此锁设置为使用公平排序策略,但是调用 tryLock() 仍将 立即获取锁(如果有可用的),而不管其他线程当前是否正在等待该锁。在某些情况下,此“闯入”行为可能很有用,即使它会打破公平性也如此。如果希望遵守此锁的公平设置,则使用 tryLock(0, TimeUnit.SECONDS),它几乎是等效的(也检测中断)。 
4. 如果当前线程已经保持此锁,则将保持计数加 1,该方法将返回 true。 
5. 如果锁被另一个线程保持,则此方法将立即返回 false 值。 
6. 指定者: 
接口 Lock 中的 tryLock 
7. 返回: 
如果锁是自由的并且被当前线程获取,或者当前线程已经保持该锁,则返回 true;否则返回 false

 

分析tryLock的源码我们发现,tryLock无论怎么样都会有返回结果,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false,并且会更改锁的计数。

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;
}

 

 

1)lock(), 拿不到lock就不罢休,不然线程就一直block。 比较无赖的做法。
2)tryLock(),马上返回,拿到lock就返回true,不然返回false。 比较潇洒的做法。
   带时间限制的tryLock(),拿不到lock,就等一段时间,超时返回false。比较聪明的做法。

3)lockInterruptibly()就稍微难理解一些。

 

先说说线程的打扰机制,每个线程都有一个 打扰 标志。这里分两种情况,
1. 线程在sleep或wait,join, 此时如果别的进程调用此进程的 interrupt()方法,此线程会被唤醒并被要求处理InterruptedException;(thread在做IO操作时也可能有类似行为,见java thread api)


2. 此线程在运行中, 则不会收到提醒。但是 此线程的 “打扰标志”会被设置, 可以通过isInterrupted()查看并 作出处理。

 

lockInterruptibly()和上面的第一种情况是一样的, 线程在请求lock并被阻塞时,如果被interrupt,则“此线程会被唤醒并被要求处理InterruptedException”。并且如果线程已经被interrupt,再使用lockInterruptibly的时候,此线程也会被要求处理interruptedException。

 

 

 

 原创公众号

     关注java 设计模式,JVM特性,

    并发编程、分布式、微服务,

   linux高可用集群,等相关技术。

        扫一扫关注我吧! 😀😀😀

                       


 

最后修改:2019-04-26 17:54:54 © 著作权归作者所有
如果觉得我的文章对你有用,请随意赞赏
扫一扫支付

上一篇

发表评论

说点什么吧~

评论列表

还没有人评论哦~赶快抢占沙发吧~