从源码看Lock特性(1)之 ReentrantLock

  多线程操作时,当出现资源竞争的场景,就需要考虑线程死锁的场景,java提供了两种方案:使用关键字 synchronized 和 Lock 类。今天来说下 jdk concurrent包的两种锁,一种是可重入锁,一种是可重入读写锁(后面再说)。

 

  • 可重入锁

  可重入锁的可重入是什么意思?我是这么理解的,以synchronized为例,A方法调用了B方法,而且A、B方法都用 synchronized 修饰,那在调用B方法时,对象锁已经被拿到了,在调用B方法时,拿不到对象锁,那么调用B方法的线程是不是需要等待,这时,A方法内部的语句还没有执行完(也就是B方法没有执行完,在等待对象锁,处于阻塞状态),是不是就出现死锁了,但是实际上并没有出现这种情况,执行方法A的线程拿到对象锁后,在执行B方法时,判断拿到对象锁的线程是自己,所以继续执行B方法的逻辑。代码如下:

  public void A(){
        synchronized(this){
            System.out.println("a method");
            B();
        }
    }
    
    public void B(){
        synchronized (this) {
            System.out.println("b method");
        }
    }

 

ReentrantLock是jdk可重入锁的实现,它有两个内部类 FairSync 和 NonFairSync 类都继承 Sync,顾名思义一个是公平锁,一个是不公平锁(也就是抢占锁)。而Sync继承于AbstractQueuedSynchronizer;AbstractQueuedSynchronizer 内部有一个 Node 类,而 Node 分为两种模式一种是共享模式,另一种是独有模式,这两种模式对应了两种锁——共享锁和排它锁。AbtsractQueuedSynchronizer 持有 head 和 tail (双向链表数据结构)的 Node,当你调用 lock 方法的时候,首先尝试获取锁,没有其它的线程获取锁了,或者锁被当前线程持有,然后会在链的末端插入一个 独占模式的Node,然后去请求锁;请求锁时,如果有线程在阻塞等待锁时,当获得锁的线程时当前线程,则本次请求锁会滑到链表的节点头判断是否时同一个线程,再给锁,否则,线程将被阻塞。主要的代码如下

 

    public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
    /**
     * Head of the wait queue, lazily initialized.  Except for
     * initialization, it is modified only via method setHead.  Note:
     * If head exists, its waitStatus is guaranteed not to be
     * CANCELLED.
     */
    private transient volatile Node head;

    /**
     * Tail of the wait queue, lazily initialized.  Modified only via
     * method enq to add new wait node.
     */
    private transient volatile Node tail;

    /**
     * The synchronization state.
     */
    private volatile int state;

 

  static final class Node {
        /** Marker to indicate a node is waiting in shared mode */
        static final Node SHARED = new Node();
        /** Marker to indicate a node is waiting in exclusive mode */
        static final Node EXCLUSIVE = null;
}

 

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

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

        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        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;
        }
    }

 

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

 

 private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }

 

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

 

posted @ 2017-04-20 11:01  風之殤  阅读(113)  评论(0)    收藏  举报