ReentrantLock显式,重入锁

方法说明

public ReentrantLock() 
创建默认的非公平锁

public ReentrantLock(boolean fair)
创建可选的公平/非公平锁 true代表公平 false代表非公平

public void lock()
获取当前锁,没有获取到则排队等待,获取到则继续向下执行

public void lockInterruptibly() throws InterruptedException
获取当前锁,没有获取到则排队等待,获取到则继续向下执行 这是一个可中断的操作,即中断可以阻止其排队

public boolean tryLock() 
尝试去获取锁,获取到返回true,没有则返回false

public boolean tryLock(long timeout, TimeUnit unit)
        throws InterruptedException
尝试在一段时间内去获取锁,获取到返回true,没有则返回false ,可中断  
     
public void unlock() 
释放当前线程持有的锁(有一个不合法释放锁的运行时异常)

public Condition newCondition() 
获取锁的Condition对象

public int getHoldCount()
获取当前线程持有锁的次数

public boolean isHeldByCurrentThread()
判断当前线程是否持有锁,是返回true,否则返回false

public boolean isLocked()
判断当前所是否被持有

public final boolean isFair()
判断当前锁是否是公平锁

public final boolean hasQueuedThreads()
判断是否有线程等待在等待队列中

public final boolean hasQueuedThread(Thread thread)
判断当前线程是否在等待队列中

public final int getQueueLength()
获取等待队列中非中断线程的个数

public boolean hasWaiters(Condition condition)
判断是否有线程等待在当前Condition队列中

public int getWaitQueueLength(Condition condition)   
获取等待在当前Condition的队列中非中断线程个数
      

原理简单说明

ReentrantLock也是基于AQS的非共享锁,state用来记录持有锁的线程加锁的次数,而AQS的等待队列用来记录没有获取到锁的线程.至于ReentrantLock对于Condition的支持,就是每一个Condition内部都有一个单向队列,用来记录等待在Condition上的线程,每当调用Condition的signal方法的时候,就是将等待在Condition队列上的头节点迁移到AQS的队列上去(signalAll就是全部迁移),等待在Condition队列上的节点是Node,等待在AQS上的节点也是同一个类Node,迁移上去以后的节点也能做正常的等待在Lock锁上的操作.这就是其简单原理

何为公平/非公平 ?
一个线程需要获得锁,如果在其获得锁以前,首先判断队列中节点是否可以获得锁,可以则当前线程进入等待,这就是公平锁,反之则为非公平锁

其实观察源码,可以发现ReentrantLock的lock方法和CountDownLatch的await方法及其类似,只要明白CountDownLatch也就懂了ReentrantLock

    public final void acquire(int arg) {
        if (
            获取锁(修改state的值)
            !tryAcquire(arg) 
            &&
            排队等待
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            触发线程中断,即修改中断标识,没有真正的出发中断异常的意味
            selfInterrupt();
    }

公平/非公平

protected final boolean tryAcquire(int acquires) {
    获取当前的线程
    final Thread current = Thread.currentThread();
    获取state的值
    int c = getState();
    判断是否第一次加锁
    if (c == 0) {
        if (
            /**
       这个方法的意义也就在于判断是否需要从队列中唤醒节点,不做这个判断就是非公平锁    
        public final boolean hasQueuedPredecessors() {
          Node t = tail; 
          Node h = head;
          Node s;
          
          return 
           等待队列是否有节点的检查
           h != t 
           &&
           
            ((
             是否有下一个节点
             s = h.next) == null 
             || 
             节点线程是否存在
             s.thread != Thread.currentThread());
      }
            */
            !hasQueuedPredecessors() 
            &&
            CAS: 设置state的值
            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");
        设置锁的持有次数,这里的state是被独占的,只能一个线程去修改state的值
        所以没有CAS操纵    
        setState(nextc);
        return true;
    }
    return false;
}

线程进去队列等待

final boolean acquireQueued(final Node node, int arg) {
    初始化失败标识
    boolean failed = true;
    try {
        初始化中断标识
        boolean interrupted = false;
        for (;;) {
            取当前节点的pre节点
            final Node p = node.predecessor();
            判断p节点是否是head节点,是否持有锁
            if (p == head && tryAcquire(arg)) {
                持有锁节点线程退出队列
                setHead(node);
                p.next = null; // help GC
                failed = false;
                return interrupted;
            }
            if (
               修改新增节点的等待状态
               shouldParkAfterFailedAcquire(p, node) &&
               park方式阻塞当前线程
                parkAndCheckInterrupt())
                interrupted = true;
        }
    } finally {
        节点回收
        if (failed)
            cancelAcquire(node);
    }
}

对于Condition本人有点要说,在学习时要注意Condition的await方法需要砍成2半来看,前一半是在操作Condition队列,后一半在操作AQS队列,因为只是简单的迁移并不能让Condition状态结束的线程还能进入到Lock状态

        public final void await() throws InterruptedException {
            if (Thread.interrupted())
                throw new InterruptedException();
            Node node = addConditionWaiter();
            int savedState = fullyRelease(node);
            int interruptMode = 0;
            while (!isOnSyncQueue(node)) {
                LockSupport.park(this);
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
            }
            上面是在操作Condition状态的节点,下面是节点被换新后执行AQS队列中的操作唤醒下一个节点,2半看的位置
            =======================================
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
            if (node.nextWaiter != null) // clean up if cancelled
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
        }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值