Reentrantlock源码剖析--菜鸟一枚,鼓励指正

Reentrantlock源码剖析(未完待续)

Reentrantlock类中大约共有21个方法,三个内部类(Syn,FairSyn,NonfairSync),实现了接口Serializable,主要的加锁解锁功能,是通过三个内部类完成的。在所有的方法中涉及到加锁的方法有三个,lock(),tryLock(),tryLock(long,TimeUnit ),涉及到解锁的一个unlock().与Condition有关的共四个方法(后续介绍),其他的函数基本上是Getter函数。


加锁

流程图:
这里写图片描述
- lock()
先看一下啊lock的源码,sync是什么?查看声明, private final Sync sync;sync是Sync的一个对象,其中Sync就是前面提到的三个内部类的其中之一,也是另外两个类的父类,Sync继承了一个听说很重要的类AbstractQueuedSynchronizer,没有具体深入,待后续跟进。

    public void lock() {
        sync.lock();
    }

lock()在Sync中是一个抽象函数,具体的定义延迟到了子类中,这样子类可以更加灵活的去实现它,这里用到了一个设计模式,模版设计模式父类声明子类实现父类调用。这里lock()函数实现的子类就是ReentrantLock中的另外两个内部类FairSyn,NonfairSync,以NonfairSync内部类进行剖析。

 final void lock() {
            //CAS操作,将state设为1,state是用来表示有无锁竞争
            if (compareAndSetState(0, 1))

            //获取锁成功,将当前的锁的所有现场设为当前线程
                                        setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);//获取失败调用acquire试图再次请求或者阻塞自己
        }

acquire()是来自AbstractQueuedSynchronizer类具体实现如下

    public final void acquire(int arg) {
    //再一次尝试获取锁若成功则返回,其中tryAcquire的实现实在子类中,若获取失败则调用acquireQueued()将加入到队列中的当前线程阻塞
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

下面是非公平锁的tryAcquire()具体实现,间接调用了nonfairTryAcquire(),首先获取state,判断当前有无线程占有该锁,c==0无竞争,通过CAS操作修改当前的state,如果c!=0在判断当前锁占有线程是不是当前线程,若是不用在获取锁,直接跟新state,此时不许用cas操作,线程安全,偏向锁。如果全部失败返回false,调用acquireQueued()将加入到队列中的当前线程阻塞。

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
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;
        }

下面是addWaiter()的函数实现,Node类是AbstractQueuedSynchronizer的一个内部类,个人理解是用来维护整个阻塞队列的。首先通过当前线程构造一个Node,获取链表的末尾节点,判断末尾节点是否存在,若pred!=null,则CAS操作直接将node插入到末尾节点后面,这里的链表是一个双向链表。若末尾节点为空,则调用enq()函数,将当前节点加入到队列中,具体看enq函数的源码。

 private Node addWaiter(Node mode) {
         //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;
    }
    private Node enq(final Node node) {
        for (;;) {//无限循环,确保当前节点入队列
            Node t = tail;
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

将当前线程入队列之后,因为获取不要锁,则需要将当前线程阻塞,acquireQueued()源码如下,在试图阻塞线程之前,每次都要重新获取一下锁,万一成功了呢,看似会死循环,我们来看一下shouldParkAfterFailedAcquire(p, node) 和parkAndCheckInterrupt()都做了什么

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 设置为空帮助GC回收
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

shouldParkAfterFailedAcquire()(ps:这里我把原来的英文注释删掉了,如果你英文好,就不要看完我在这瞎bb了,直接看原注释就行,比我说的好。)首先查看了前一个node的节点状态:

  • 规则1:如果前继的节点状态为SIGNAL,表明当前节点需要unpark,则返回成功,此时acquireQueued方法的第12行(parkAndCheckInterrupt)将导致线程阻塞
  • 规则2:如果前继节点状态为CANCELLED(ws>0),说明前置节点已经被放弃,则回溯到一个非取消的前继节点,返回false,acquireQueued方法的无限循环将递归调用该方法,直至规则1返回true,导致线程阻塞
  • 规则3:如果前继节点状态为非SIGNAL、非CANCELLED,则设置前继的状态为SIGNAL,返回false后进入acquireQueued的无限循环,与规则2同
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            return true;
        if (ws > 0) {
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return     private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }
  • tryLock()

  • tryLock(long,TimeUnit )

解锁

流程图
这里写图片描述
-unlock()

注释:
SIGNAL(-1) :线程的后继线程正/已被阻塞,当该线程release或cancel时要重新这个后继线程(unpark)
CANCELLED(1):因为超时或中断,该线程已经被取消
CONDITION(-2):表明该线程被处于条件队列,就是因为调用了Condition.await而被阻塞
PROPAGATE(-3):传播共享锁
0:0代表无状态

如果你看完了,还不知道我再说什么,看下面这个帖子
http://www.cnblogs.com/MichaelPeng/archive/2010/02/12/1667947.html

你可能感兴趣的:(java源码剖析,java,Reentrant,lock)