AQS 之张三占坑位(一)

AQS 之张三占坑位(一)

涉及到锁,那么必定涉及到等待。AQS就是用来做锁和排队使用。这里有个小故事分享一下:

​ 张三在某大型商场跟女朋友逛街,突然尿意来袭急奔厕所,发现坑位空位立马进入锁上大门。

​ 三分钟后,李四走进厕所发现有一个坑位,过去拉门发现大门紧锁并且里面大喊有人,李四没办法只能旁边找了个凳子坐了下来,刚坐下来发现还有个人,李四心想完了还有人排在我前面,结果那人说自己是锁魂典狱长不是去上厕所的让他大可放心,李四才稍微放下了心继续看了下坑位大门紧闭,转头问典狱长:“里面真的有人吗?”,“有人!”,典狱长说道。听到这里李四还不放心,又问了一遍有人吗,典狱长不耐烦的说:“我说了有人,你问几遍了!”。这时候李四才真正的坐了下来。但是典狱长耐心消失殆尽 …

​ 这时候又过了三分钟,只见进来一个叫王五的人,他跟李四做了一样的操作,然后坐到了李四旁边。这时候万众瞩目张三把门打开从坑位里出来了,典狱长一看两眼放光,耐心又回来了,心想我终于可以离开这破地方了。他拉着李四说:老哥,坑位有了!坑位有了!李四一听直接起身去占领坑位。完全不顾身后留下的魂魄,典狱长微微一笑把位置交给了李四的魂魄然后扬长而去,从此李四的魂魄变成了典狱长,王五上位了…

栗子
    private static ReentrantLock lock =  new ReentrantLock();

    public static void main(String[] args) {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3,4,10,TimeUnit.SECONDS,
                new LinkedBlockingQueue(500),new DefaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy());
        threadPoolExecutor.execute(()->{
            lock.lock();
            try {System.out.println(Thread.currentThread().getName() + " 张三占领了坑位");
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {lock.unlock();}
        });
        threadPoolExecutor.execute(()->{
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + " 李四占领了坑位");
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {lock.unlock();}
        });
        threadPoolExecutor.execute(()->{
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + " 王五占领了坑位");
            }finally {lock.unlock();}
        });
    }
李四抢坑位
    java.util.concurrent.locks.ReentrantLock.class
        //抢占坑位
        public void lock() {
            sync.lock();
        }

		//------------------------------
    
		//抢占坑位
        final void lock() {
            //张三来的时候坑位门没有关,直接进坑位
            if (compareAndSetState(0, 1))
                //这里表示张三占住了坑位
                setExclusiveOwnerThread(Thread.currentThread());
            else
                //李四来了坑位没了
                acquire(1);
        }

		//------------------------------

		//占坑位
        public final void acquire(int arg) {
            //李四想抢坑位
            if (!tryAcquire(arg) &&
                //李四被迫坐下
                acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
                selfInterrupt();
        }

		//------------------------------
		
		//尝试抢占坑位
        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;
        }

		//------------------------------

		//李四坐下
        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;
        }

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

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

		//------------------------------

        private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
            int ws = pred.waitStatus;
            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
                compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
            }
            return false;
        }

		//老典狱长跑路了,李四的灵魂留下当了典狱长
        private void setHead(Node node) {
            head = node;
            node.thread = null;
            node.prev = null;
        }

张三释放坑位
    //张三释放坑位
    public void unlock() {
        sync.release(1);
    }
    
		//------------------------------

    public final boolean release(int arg) {
    	//走了一个张三分身
        if (tryRelease(arg)) {
            Node h = head;
            //典狱长是不是没有耐心了
            if (h != null && h.waitStatus != 0)
                //通知
                unparkSuccessor(h);
            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) {
                free = true;
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }


		//------------------------------


	//李四觉醒!
    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.
         */
        //s就是李四
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        //李四觉醒了!
        if (s != null)
            LockSupport.unpark(s.thread);
    }
    
总结

其中张三线程先来,直接就使用了资源,并且站住坑位 status + 1 , 坑位setExclusiveOwnerThread 设置成张三本人,之后李四来了,发现坑位计数器status有人,并且exclusiveOwnerThread 还不是自己,李四乖乖坐下,系统分配了一个傀儡Node 给李四当头结点head,让李四当尾节点tail,傀儡Node和李四相互有pre和next,等到张三走了status - 1,setExclusiveOwnerThread = null 。李四上位status + 1 ,setExclusiveOwnerThread = 李四,傀儡死掉,李四的Node变成了傀儡Node。

上面的代码是不公平的占坑位,因为每个人进来都是先抢坑位,不管有没有人在排队。

实现公平锁的方式就是占坑位的时候先看看有没有人在等坑位即可。其余都一样操作。

未完待续。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值