Erlo

Java并发(九):重入锁 ReentrantLock

时间:2018-11-09 14:31   阅读:12次   来源:博客园页面报错

一、ReentrantLock类结构

public class ReentrantLock implements Lock, java.io.Serializable {
    private final Sync sync; // 锁 大部分功能都是委托给Sync来实现的
    abstract static class Sync extends AbstractQueuedSynchronizer {}
    static final class FairSync extends Sync {}
    static final class NonfairSync extends Sync {}
}

二、以NonfairSync为例解析重入锁

获取锁标志:

(NonfairSync extends Sync extends AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer)

1.AbstractQueuedSynchronizer.state>0(0代表没有被占用,大于0代表有线程持有当前锁(锁可以重入,每次重入都+1))

2.AbstractOwnableSynchronizer.exclusiveOwnerThread == Thread.currentThread()

获取锁:

public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();// 默认是非公平锁
        lock.lock();
    }
    
    // ReentrantLock
    public void lock() {
        sync.lock();
    }
    
    // NonfairSync
    final void lock() {
        if (compareAndSetState(0, 1)) // 尝试获取锁
            setExclusiveOwnerThread(Thread.currentThread()); // 如果拿到锁就设置当前线程 
        else
            acquire(1);
    }
    
    // AbstractQueuedSynchronizer
    public final void acquire(int arg) {
        if (!tryAcquire(arg) && // 尝试获取锁
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 没有获取到锁,将线程加入同步队列(参考上一篇AbstractQueuedSynchronizer)
            selfInterrupt();
    }
    
    // NonfairSync
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
    
    #zs#*
     * 获取锁标志:
     * (NonfairSync extends Sync extends AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer)
     * 1.AbstractQueuedSynchronizer.state>0(0代表没有被占用,大于0代表有线程持有当前锁(锁可以重入,每次重入都+1))
     * 2.AbstractOwnableSynchronizer.exclusiveOwnerThread == Thread.currentThread()
     * Sync(NonfairSync没有重写nonfairTryAcquire)
     #fzs#
    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;
    }

释放锁:

    // ReentrantLock
    public void unlock() {
        sync.release(1);
    }
    
    // AbstractQueuedSynchronizer
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h); // 唤醒队列下一个节点线程 参考上一篇:AbstractQueuedSynchronizer
            return true;
        }
        return false;
    }
    
    // Sync
    protected final boolean tryRelease(int releases) {
        int c = getState() - releases;
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        boolean free = false; // 重入锁,直到state==0才算释放
        if (c == 0) {
            free = true;
            setExclusiveOwnerThread(null);
        }
        setState(c);
        return free;
    }

三、公平锁与非公平锁

    // FairSync(NonfairSync会先尝试拿锁,FairSync不会)
    final void lock() {
        acquire(1);
    }

    // FairSync
    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            if (!hasQueuedPredecessors() && // CLH队列为空或者队列头结点是当前线程节点 才能获得锁
                compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }
    
    #zs#*
     * AbstractQueuedSynchronizer
     * true - CLH队列为空或者队列头结点是当前线程节点
     #fzs#
    public final boolean hasQueuedPredecessors() {
        Node t = tail;
        Node h = head;
        Node s;
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }

以上代码可以看出,公平锁和非公平锁只有两处不同:

(1)非公平锁在调用 lock 后,首先就会调用 CAS 进行一次抢锁,如果这个时候恰巧锁没有被占用,那么直接就获取到锁。

(2)非公平锁在 CAS 失败后,和公平锁一样都会进入到 tryAcquire 方法。在 tryAcquire 方法中,如果发现锁这个时候被释放了(state == 0),非公平锁会直接 CAS 抢锁,而公平锁会判断等待队列是否有线程处于等待状态,如果有则不去抢锁,乖乖排到后面。

公平锁和非公平锁就这两点区别,如果这两次 CAS 都不成功,那么后面非公平锁和公平锁是一样的,都要进入到阻塞队列等待唤醒。

因此,非公平锁会有更好的性能,因为它的吞吐量比较大。当然,非公平锁让获取锁的时间变得更加不确定,可能会导致在阻塞队列中的线程长期处于饥饿状态。

四、ReentrantLock优势

ReentrantLock与synchronized具有相同的功能和内存语义。

1、与synchronized相比,ReentrantLock提供了更多,更加全面的功能,具备更强的扩展性。例如:时间锁等候,可中断锁等候,锁投票。

2、ReentrantLock还提供了条件Condition,对线程的等待、唤醒操作更加详细和灵活,所以在多个条件变量和高度竞争锁的地方,ReentrantLock更加适合。

3、ReentrantLock提供了可轮询的锁请求。它会尝试着去获取锁,如果成功则继续,否则可以等到下次运行时处理,而synchronized则一旦进入锁请求要么成功要么阻塞,所以相比synchronized而言,ReentrantLock会不容易产生死锁些。

4、ReentrantLock支持更加灵活的同步代码块,但是使用synchronized时,只能在同一个synchronized块结构中获取和释放。注:ReentrantLock的锁释放一定要在finally中处理,否则可能会产生严重的后果。

5、ReentrantLock支持中断处理,且性能较synchronized会好些。

 

参考资料 / 相关推荐

【死磕Java并发】—–J.U.C之重入锁:ReentrantLock

一行一行源码分析清楚AbstractQueuedSynchronizer

Java并发(八):AbstractQueuedSynchronizer

下一篇:java导入excle表格,并且对表格进行相应的修改,并...

评论留言

还没有评论留言,赶紧来抢楼吧~~

Erlo大厅()

* 这里是“Erlo大厅”,在这发言所有人都可以看到。只保留当天信息

  • Erlo.vip2018-11-18 00:26:36Hello、欢迎使用Erlo大厅,这里是个吐槽的地方。
  • 首页 源码分享 新闻资讯 ERLO 搜索
    鼠标试试
    返回顶部