Semaphore源码解析

先看到Semaphore的构造方法

	//该方法默认非公平锁
	public Semaphore(int permits) {
        sync = new NonfairSync(permits);
    }
    public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }

看到Semaphore的acquire方法

	public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        //如果被打断了,抛出中断异常
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }

tryAcquireShared方法

		//FairSync公平锁的实现
		protected int tryAcquireShared(int acquires) {
			//死循环
            for (;;) {
            	//判断队列中是否有线程在排队等待
                if (hasQueuedPredecessors())
                	//有其他线程在排队,则获取锁失败
                    return -1;
                //获取state的值
                int available = getState();
                //获取剩余值
                int remaining = available - acquires;
                //如果剩余值小于0 则 返回剩余值, 表示获取锁失败
                //如果剩余值大于等于0 , 则cas修改state的值,cas成功,返回剩余值,获得锁成功
                //cas失败则下一次循环
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }
        //NonfairSync非公平锁的实现
        final int nonfairTryAcquireShared(int acquires) {	
        	//对比上面的公平锁,不会判断有没有线程在排队。
            for (;;) {
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }

doAcquireSharedInterruptibly方法

	//doAcquireShared方法之前文章讲过的代码了,不做赘述了,这里的doAcquireSharedInterruptibly只是会响应中断,抛出中断异常,其他的是一样的。我们针对Semaphore分析r为0的情况,之前的解析的读写锁返回的r只能是-1和1
	private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        //将线程包装为node节点,进入等待队列,修改前置节点的waitStatus,休眠前如果前置节点是头结点,尝试抢锁
        
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    //这里r返回的是扣减后的state的值,可以是0
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                    	//假设线程被头结点唤醒。执行到这里
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    //如果被打断了,直接抛出中断异常
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

setHeadAndPropagate方法也在读写锁的文章讲过了,这里再借着Semaphore看下Node.PROPAGATE

	private void setHeadAndPropagate(Node node, int propagate) {
		//先获取head
        Node h = head; 
        setHead(node);
        //这里propagate可能为0
        //在判断头结点的waitStatus , 这里头结点的状态很可能为0, 唤醒了node 把自己的waitStatus 修改成了0
        //(h = head) == null 重新获取头结点(可能是node节点也可能不是),此时该节点的waitStatus可能不是0(还有其他节点再node后面排队),那就执行if里面的代码。
        //也可能node节点的waitStatus也是0,但是有可能队列中是有其他共享节点排队的 
        //比如另一个线程(叫做B线程)执行了addWaiter进入队列,还没来得及执行shouldParkAfterFailedAcquire修改头节点(node)的waitStatus为-1 , 此时这里的doReleaseShared方法就不会被调用了。
        //接着B线程正常执行 ,然后有A线程释放了资源(此时A线程在执行releaseShared的doReleaseShared方法),但是此时来了C线程修改state成功了抢到了锁,然后B线程修改头结点的waitStatus为-1,然后再次抢锁,发现此时state为0,B线程去阻塞
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
        	//重新获取一次头节点(可能是node节点也可能不是)
            (h = head) == null || h.waitStatus < 0) {
            Node s = node.next;
            if (s == null || s.isShared())
                doReleaseShared();
        }
    }

看到Semaphore的release方法

	public void release() {
        sync.releaseShared(1);
    }
    public final boolean releaseShared(int arg) {
    	//恢复state的值
        if (tryReleaseShared(arg)) {
        	//唤醒节点
            doReleaseShared();
            return true;
        }
        return false;
    }

tryReleaseShared方法

		protected final boolean tryReleaseShared(int releases) {
            for (;;) {
                int current = getState();
                int next = current + releases;
                if (next < current) // overflow
                    throw new Error("Maximum permit count exceeded");
                //cas恢复state的值,死循环,直到成功,返回true
                if (compareAndSetState(current, next))
                    return true;
            }
        }

doReleaseShared方法也在读写锁的文章讲过了,这里再借着Semaphore看下Node.PROPAGATE

	private void doReleaseShared() {
        //1---A线程执行下面方法
        for (;;) {
        	//1---获取头结点
            Node h = head;
            if (h != null && h != tail) {
            	//1---假设此时B线程还没来得及修改头节点的waitStatus从0更新为-1
                int ws = h.waitStatus;
                //2---第二次循环A线程获取头结点状态为SIGNAL(-1)
                if (ws == Node.SIGNAL) {
                	//2---cas修改头结点为0
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    //2---cas成功则唤醒B线程,此时B线程醒来去获取锁,实际上如果state没有被其他线程释放回复,B线程也不一定能抢到锁。
                    //这么看来PROPAGATE作用也就是让doReleaseShared方法更有机会去唤醒睡眠的线程,使得排队的线程在允许的情况下更早被唤醒。
                    unparkSuccessor(h);
                }
                //1---则走了这里,cas修改头结点的waitStatus为-3,但是被B线程抢先修改成了-1,然后B线程睡眠了
                //1---此时cas失败,然后进入下一次循环
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                //从网上搜索其他文章可以看到说Node.PROPAGATE可以防止队列中有线程不被唤醒, 实际上在jdk8的代码来说 setHeadAndPropagate方法在旧header的waitStatus状态不小于0的情况下,会重新获取新的header的waitStatus再进行判断。并且(shouldParkAfterFailedAcquire方法)进入队列准备去排队的节点第一次修改前置节点的waitStatus为-1时,不会去睡眠,还会再去尝试抢锁,获取不到再去睡眠(此时前置节点的waitStatus一定为-1),不会存在队列中有线程不被唤醒的情况。
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }

到此Semaphore源码也就解析完了,其实主要是来看Node.PROPAGAT的QAQ

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值