ReadWriteLock源码阅读之WriteLock加锁解锁过程源码分析

写的有点乱,因为无论是ReadWriteLock还是ReentrantLock加锁解锁过程本身就很乱。
只写了lock方法,lockInterruptibly方法其实和lock差不多,只是再睡眠的时候被打断了醒来后发现被打断了就抛出了一个异常,其余没什么差别。
首先读写锁也是有公平和非公平之分。这一点要注意。默认是非公平锁。

加锁

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

tryAcquire方法

protected final boolean tryAcquire(int acquires) {
            /*
             * Walkthrough:
             * 1. If read count nonzero or write count nonzero
             *    and owner is a different thread, fail.
             * 2. If count would saturate, fail. (This can only
             *    happen if count is already nonzero.)
             * 3. Otherwise, this thread is eligible for lock if
             *    it is either a reentrant acquire or
             *    queue policy allows it. If so, update state
             *    and set owner.
             */
             //当前线程
            Thread current = Thread.currentThread();
            //是否上锁
            int c = getState();
            int w = exclusiveCount(c);
            //因为写锁是互斥锁,只要有读锁或者写锁都不能上锁成功
            //除非重入,c!=0表示已经有人上锁了
            if (c != 0) {
                // (Note: if c != 0 and w == 0 then shared count != 0)
                // 如果w == 0 表示没有人加写锁,此时是读锁,只有
                // 重入可以枷锁成功。			                       
                //不过我不推荐读写锁读锁升级为写锁,会导致死锁。
                //因为互斥,所以只要线程不相同不能重入,就返回false
                if (w == 0 || current != getExclusiveOwnerThread())
                    return false;
                //重入的情况,大于重入的最大值直接溢出报错。
                if (w + exclusiveCount(acquires) > MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                //直接上锁,不需要cas,因为这个是重入写锁加写锁情况。
                setState(c + acquires);
                return true;
            }
            //writeShouldBlock方法在下面有介绍
            //当他不需要排队,或者说公平锁情况下,会直接进行cas操作,将c也就是state由0改为1
            //如果加锁失败,整个方法返回false;
            //这个地方主要是当他发现当前无人持有锁,得去看有没有
            //其他线程也在等
            if (writerShouldBlock() ||
                !compareAndSetState(c, c + acquires))
                return false;
            //如果加锁成功,将线程设到AbstractOwnableSynchronizer这个里面去
            setExclusiveOwnerThread(current);
            return true;
        }

exclusiveCount方法

static final int SHARED_SHIFT   = 16;
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
//相当于c与 16个1。这个地方计算的是当前写锁的情况
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }

writerShouldBlock 方法

//公平锁,判断自己需不需要排队
final boolean writerShouldBlock() {
			//这个方法我之后如果有时间写ReentrantLock的时候再详细说吧,
			//他其实是aqs里面很经典很重要的一个方法,就是判断自己需不需要排队
            return hasQueuedPredecessors();
        }
//非公平锁,直接抢资源就完事了
final boolean writerShouldBlock() {
            return false; // writers can always barge
        }

addWaiter方法

aqs经典方法

private Node addWaiter(Node mode) {
		//new 出一个node,
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        //拿到aqs的尾节点。
        Node pred = tail;
        //如果尾节点不为null,说明aqs已经被初始化
        if (pred != null) {
        	//就是节点指向,链表维护。
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        //如果尾节点为null,或者cas失败(表示同一个时间点,多个线程cas执行,只成功一个)
        //往下看enq方法
        enq(node);
        return node;
    }

enq方法

aqs经典方法

 private Node enq(final Node node) {
        for (;;) {
        	//拿到尾节点
            Node t = tail;
            //如果为空,则说明队列未初始化
            if (t == null) { // Must initialize
            	//初始化队列。
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
            	//这个和上一个的入队操作一样,注意这个地方是死循环,哪怕几千个
            	//线程同时cas去抢资源,无线循环也会入队完成。
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

acquireQueued 方法

之前这个node已经入队了。

final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
            //拿到node的前一个节点
                final Node p = node.predecessor();
            //如果上一个节点是aqs的头节点,再尝试加锁一次,避免park,真牛这代码
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                //第一次shouldParkAfterFailedAcquire为false,继续来一次。
                //第二次p.waitstatus变成-1,则准备park了。
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    //解锁成功后,不管是不是false还是true都进入下一轮循环。
                    //去尝试加锁,去竞争。
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

shouldParkAfterFailedAcquire方法

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
		//拿到前一个节点waitStatus默认是0
        int ws = pred.waitStatus;
        //第二次就是-1.返回true
        if (ws == Node.SIGNAL)
            /*
             * This node has already set status asking a release
             * to signal it, so it can safely park.
             */
            return true;
        if (ws > 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 must be 0 or PROPAGATE.  Indicate that we
             * need a signal, but don't park yet.  Caller will need to
             * retry to make sure it cannot acquire before parking.
             */
             //将他改成-1退出为false
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }

parkAndCheckInterrupt方法

 private final boolean parkAndCheckInterrupt() {
 		//自旋
        LockSupport.park(this);
        //这个其实再这个地方没什么卵子用,lockInterruptibly()这个方法的时候,后面直接抛异常
        //dog李这里就复用了这一段代码。可读性较差(唯一能蔑视大神的点了哈哈哈)
        //解锁成功后直接返回
        return Thread.interrupted();
    }

解锁

public void unlock() {
            sync.release(1);
        }

release方法

aqs的方法之一

  public final boolean release(int arg) {
  		//把state置为0之后
        if (tryRelease(arg)) {
        	//拿到头节点
            Node h = head;
            //如果头节点的waitStatus != 0 就unpark他下一个节点。
            //这里注意,waitStatus 初始化为0,如果不为0,说明
            //有第二个线程把他置为-1 或者什么什么的,说明有人排队
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

tryRelease方法

 protected final boolean tryRelease(int releases) {
 			//解锁的线程不是当前的线程。抛异常。
            if (!isHeldExclusively())
                throw new IllegalMonitorStateException();
            //把之前的写锁的后16位去-1.
            int nextc = getState() - releases;
            //如果这个时候是0,表示没人加锁。
            boolean free = exclusiveCount(nextc) == 0;
            //把持有锁的线程置位null
            if (free)
                setExclusiveOwnerThread(null);
            setState(nextc);
            return free;
        }

unparkSuccessor 方法

aqs方法之一

private void unparkSuccessor(Node node) {
        /*
         * If status is negative (i.e., possibly needing signal) try
         * to clear in anticipation of signalling.  It is OK if this
         * fails or if status is changed by waiting thread.
         */
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        /*
         * Thread to unpark is held in successor, which is normally
         * just the next node.  But if cancelled or apparently null,
         * traverse backwards from tail to find the actual
         * non-cancelled successor.
         */
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            //这里我也不清楚为什么循环,可能他只是想waitStatus<0的节点。
            //我猜测之前waitStatus再某个地方某个逻辑(可能是取消的时候)设置成>0了。
            //果然顺着源码去读,验证猜测。
            /** waitStatus value to indicate thread has cancelled 
        static final int CANCELLED =  1;*/
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        // s != null 就去唤醒他
        if (s != null)
            LockSupport.unpark(s.thread);
    }
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值