Java 并发编程之ReentrantLock源码解析

  上一篇中,我们在源码的角度讲了AbstractQueuedSynchronizer的原理,链接为 Java 并发编程之AbstractQueuedSynchronizer源码解析 ,这一篇中,我们从源码的角度讲一下 ReentrantLock 的原理。

  我们知道,ReentrantLock是我们在jdk中经常使用的可重入锁的实现类,内部分为公平的可重入锁以及不公平的可重入锁,那么这些机制是如何实现的呢?

一、ReentrantLock如何实现的可重入锁?

  可重入锁,顾名思义,即当我这个线程拿到锁之后,即调用lock()方法,继续调用lock()方法也可以再次获取到锁。当然调用多少次lock()就需要调用多少次unLock()来释放锁。

  我们看一下ReentrantLock中公平锁与不公平锁的抽象父类:

    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

 
 		. . .
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();

            // 如果为0,说明是第一次获取锁
            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");
                // 如果是,那么直接设置状态为 c + acquires
                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);
            }
            // 当为拿到锁的线程时,直接置为相应state
            setState(c);
            return free;
        }

    }

  可以看到,nonfairTryAcquire()方法来获取锁的时候,增加了是否是当前线程的判断,就是说ReentrantLock中增加了拿到锁的线程标示位。当这个线程再次获取锁的时候,直接增加state即可。

 

二、ReentrantLock的公平锁与不公平锁的实现:

  ReentrantLock如何实现的公平锁与不公平锁?

  1、不公平锁的实现:

    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

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

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

      可以看到,lock()方法直接先试着获取了锁,如果获取不到,直接调用 Sync 的 nonfairTryAcquire()方法来获取锁。这样一个线程A获取到锁并释放之后,即使现在有很多线程在等待,此线程A仍有可能再次获取到锁。

 

  2、公平锁的实现:

   static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            acquire(1);
        }


        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    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;
        }
    }

  可以看到,FairSync直接实现了 tryAcquire() 这个方法,比较重要的就是  !hasQueuedPredecessors() 这个判断,即当c为0的时候,会有 !hasQueuedPredecessors() 这个判断,这个判断是做什么的呢?我们看一下源代码:

    /**
     * 判断是否有线程等待时间比当前线程等待时间更长
     */
    public final boolean hasQueuedPredecessors() {

        Node t = tail; // Read fields in reverse initialization order
        Node h = head;
        Node s;
        return h != t &&
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }

  即判断是否有线程等待时间比当前线程等待时间更长,那么我们看上述例子,当线程A获取到锁之后,此时线程B、线程C排队等待获取锁。那么当线程A释放锁之后,线程A再次获取到锁,hasQueuedPredecessors()会返回true,因为此时此时有线程在排队,且等待时间更长,此时只有线程B调用hasQueuedPredecessors()方法才返回false,也就是说,只有线程B才能获取到锁。这样公平锁即实现了。

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值