`

ReentrantLock

 
阅读更多

ReentrantLock 是一个可重入的锁,用于线程之间的同步。 

采用独占锁方式,即一个线程获得锁,其它线程需等待锁的释放。

ReentrantLock与内置锁synchronized相比,多了:

1.lockInterruptibly() 获取锁的过程中 可以响应中断并返回假

  2. tryLock(long timeout, TimeUnit unit)  获取锁的过程中,可以响应中断并返回假、支持超时检测并返回假,

  3.可以不在一个代码中,但要保证锁的释放

 

    jdk1.5 中synchronized 性能没有 ReentrantLock好,jdk1.6中就差不多了。

 

class X {
   private final ReentrantLock lock = new ReentrantLock();

   public void m() { 
     lock.lock();  // 当前线程如果能够获得锁,则继续执行,否则阻塞,等待其它线程释放锁并得到锁
     try {
       // ... method body
     } finally {
       lock.unlock();//已获取锁,执行完逻辑,释放锁,注意,需要确保锁的释放,否则其它线程会一直阻塞中。
     }
   }
 }

 方法:

 

 int getHoldCount()
          查询当前线程保持此锁定的次数。
protected  Thread getOwner()
          返回目前拥有此锁定的线程,如果此锁定不被任何线程拥有,则返回 null
protected  Collection<Thread> getQueuedThreads()
          返回一个 collection,它包含可能正等待获取此锁定的线程。
 int getQueueLength()
          返回正等待获取此锁定的线程估计数。
protected  Collection<Thread> getWaitingThreads(Condition condition)
          返回一个 collection,它包含可能正在等待与此锁定相关给定条件的那些线程。
 int getWaitQueueLength(Condition condition)
          返回等待与此锁定相关的给定条件的线程估计数。
 boolean hasQueuedThread(Thread thread)
          查询给定线程是否正在等待获取此锁定。
 boolean hasQueuedThreads()
          查询是否有些线程正在等待获取此锁定。
 boolean hasWaiters(Condition condition)
          查询是否有些线程正在等待与此锁定有关的给定条件。
 boolean isFair()
          如果此锁定的公平设置为 true,则返回 true。
 boolean isHeldByCurrentThread()
          查询当前线程是否保持此锁定。
 boolean isLocked()
          查询此锁定是否由任意线程保持。
 void lock()
          获取锁定。
 void lockInterruptibly()
          如果当前线程未被中断,则获取锁定。
 Condition newCondition()
          返回用来与此 Lock 实例一起使用的 Condition 实例。
 String toString()
          返回标识此锁定及其锁定状态的字符串。
 boolean tryLock()
          仅在调用时锁定未被另一个线程保持的情况下,才获取该锁定。
 boolean tryLock(long timeout, TimeUnit unit)
          如果锁定在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁定。
 void unlock()
          试图释放此锁定。

 

 具体实现是通过类AbstractQueuedSynchronizer 来实现。

 

AbstractQueuedSynchronizer 是多线程锁实现的基类,采用先进先出线程队列,队列中存有想要获得锁的线程,先入队列的线程先获得锁,当它释放锁之后,后进入队列的再获得锁,以此类推。 

 

内部成员变量:state ,定义当前锁定次数。0 未被锁定,一个线程可以锁定N次。 当一个线程已经有锁时,再调用锁定时,不需要争抢锁,state+=1,记录锁的次数。释放锁时必须释放N次才能真正释放锁。

由Node对象组成一个等待获得锁的链表。

 

 基本原理: 当线程想要获取锁,如果state ==0则锁未被占用,可以获得锁,如果state>0则锁被占用,将当前线程生成Node对象放到链表尾部,调用Unsafe.park阻塞当前线程。 当已获得锁的线程释放锁,从链表头部找需要锁的线程,调用Unsafe.unpark 恢复线程 。

 

类图:



 

 公平锁:FairSync类实现,按照线程请求锁的先后顺序时获得锁,即使请求瞬间锁未被占用,但队列中有其它线程等待锁,则当前线程也不会去争抢锁。 

非公平锁:NoFairSys类实现,当前线程要获得锁,如果锁未被获得(可能上一个持有锁的线程已释放锁,队列中阻塞线程已恢复,处于就绪状态,但还未获得锁时),当前线程会与其它线程争抢锁,可能会抢到锁,获得执行程序机会。 如果当前线程执行时间较短,执行完成,锁释放后,被阻塞恢复的线程才来获得锁,这相当于两个线程之间又多执行了一条线程,性能比公平锁好一些。

 

 非公平锁Lock时序图:



 

非公平锁Lock说明:

 

 final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }
    compareAndSetState ,是AbstractQueuedSynchronizer的方法,用CAS(compareAndSwap)方式设定state, compareAndSetState(0,1) 当state==0(没有线程占用锁,有可能持有锁的线程刚释放锁)时,设为1,这时可能有其它线程(队列中被阻塞线程已被恢复(unpark)或刚执行到此处的其它线程)也在争抢锁。

 

     如果返回真,则表示当前线程抢占了锁,则其它线程进阻塞。

     如果返回假:则调用 acquire方法。

 

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

 

 

 

tryAcquire 试图再去获取锁,tryAcquire中调用 nonfairTryAcquire 方法,由子类Sync类实现:

 

 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()) {//如果当线程已经获得了锁,再次锁定时计器state+1,不需要在争抢锁了,因为之前已持有锁
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;//返回真,表示当前线程获得销
            }
            return false;//抢锁失败
        }

 

 

 

如果tryAcquire方法返回假,即没有抢到锁,则执行 addWaiter方法,将线程存入链表尾部。

private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // 把当前线程存入链表尾部
        Node pred = tail;//为什么不直接使用共享变量tail??可能volatile型变量每次读取直接访问内存,可以通过赋值引用,在线程缓存中新生成一个引用,每次访问这个引用,速度快。
共享变量可能随时改变,比如先后调用二次成员变量上的方法,则有可能在方法之间,成员变量对象已被其它线程改变了。

        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {//给共享成员变量赋值使用原子方法

                pred.next = node;//线程安全??上一行最多只有一条线程会返回真,即本行代码最多只能被一条线程执行,是线程安全的。即使执行本行前,线程被调度挂起,其它线程执行,改变了TAIL,线程恢复时,再执行,由于 局部变量pred是之前tail的引用值,不会随当前tail值变化,所以是正确的。
                return node;
            }
        }
        enq(node);
        return node;
    }

 

当前线程加入队列中之后,进入 acquireQueued方法

  final boolean acquireQueued(final Node node, int arg) {
        try {
            boolean interrupted = false;
            for (;;) {//死循环,直至线程获得独占锁,包括(刚进入的线程或阻塞之后恢复的线程)
                final Node p = node.predecessor();//得到当前线程的前一个节点
                if (p == head && tryAcquire(arg)) {//如果前一节点为HEAD,则试着获取锁,得到锁时,把自己置为HEAD
                    setHead(node);//线程安全?? 只有一条线程能获得锁,执本本行代码
                    p.next = null; // help GC
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())//parkAndCheckInterrupt中调用 LockSupport.park(this),线程恢复后会从下一行代码继续执行。
                    interrupted = true;
            }
        } catch (RuntimeException ex) {
            cancelAcquire(node);
            throw ex;
        }
    }

 

 

  private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int s = pred.waitStatus;
        if (s < 0)
            /*
             * This node has already set status asking a release
             * to signal it, so it can safely park
             */
            return true;
        if (s > 0) {
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             */
	    do {
		node.prev = pred = pred.prev;
	    } while (pred.waitStatus > 0);//链表中去除已经被取消的线程
	    pred.next = node;
	}
        else
            /*
             * 设定前一个节的waitStatus=Node.SIGNAL,表示本节点阻塞需要unpark,unpark的任务交给前一个节点的release完成。
             */
            compareAndSetWaitStatus(pred, 0, Node.SIGNAL);
        return false;
    }
 

 

 

 锁的释放:

 

 

 public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)//h.waitStatus=0没有后续节点??
                unparkSuccessor(h);//找到head之后的需要解锁的线程,调用 LockSupport.unpark(s.thread);进行解锁
            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) {//锁定次数为0,是真正释放锁,否则只是释放了一层锁,当前线程还是持有锁
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }
 

 

 

 

  • 大小: 289.4 KB
  • 大小: 50.1 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics