Semaphore(信号灯)使用和源码

          Semaphore(信号灯):一个计数信号量。顾名思义,一个信号量拥有一定数量的许可证,一个线程acquire需要申请到许可证才能获取锁,否则阻塞等待;释放的时候会返还许可证给阻塞的线程申请,常用于限制可以访问某些资源(物理或逻辑的)线程数目,限流场景等。

          通过测试代码,看一下Semaphore的基本用法

public class Test {
    public static void main(String[] args) {
        int N = 8;            //工人数
        Semaphore semaphore = new Semaphore(5); //机器数目
        for(int i=0;i<N;i++)
            new Worker(i,semaphore).start();
    }
 
    static class Worker extends Thread{
        private int num;
        private Semaphore semaphore;
        public Worker(int num,Semaphore semaphore){
            this.num = num;
            this.semaphore = semaphore;
        }
 
        @Override
        public void run() {
            try {
                semaphore.acquire();
                System.out.println("工人"+this.num+"占用一个机器在生产...");
                Thread.sleep(2000);
                System.out.println("工人"+this.num+"释放出机器");
                semaphore.release();           
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
} 

 

      结果明显彰显了信号灯的作用,然为什么它有这个功能呢?我们来看看Semaphore的源码

           Semaphore的构造

//默认非公平
public Semaphore(int permits) {
    sync = new NonfairSync(permits);
}
//自定义公平性
public Semaphore(int permits, boolean fair) {
    sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

            默认的构造为例

    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = -2694183684443567898L;

        NonfairSync(int permits) {
            super(permits);
        }

        protected int tryAcquireShared(int acquires) {
            return nonfairTryAcquireShared(acquires);
        }
    }

           super(permits)执行

        Sync(int permits) {
           //设置AQS中的state的值
            setState(permits);
        }

           上面构造完成后,semaphore.acquire()  执行

           代码最后到了AbstractQueuedSynchronizer(AQS)

    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }

            tryAcquireShared方法  执行

        final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                //通过自旋,返回state减acquires的值
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    //通过cas改变它的值
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }

          tryAcquireShared的结果如果小于0,代表位子已经被占满了,doAcquireSharedInterruptibly  执行

private void doAcquireSharedInterruptibly(int arg)
    throws InterruptedException {
    //增加到AQS队列中
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        for (;;) {
            final Node p = node.predecessor();
            if (p == head) {
                //再次尝试获得位子
                //如果信号等数为2,则代表2个位子
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    //进入if代表还有位子
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
            }
            //shouldParkAfterFailedAcquire 
            //代表Acquire失败后尝试去park线程
            if (shouldParkAfterFailedAcquire(p, node) &&
                //park线程
                parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
          semaphore.release() 执行释放位子  代码执行到达
    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }

               tryReleaseShared  执行

    protected final boolean tryReleaseShared(int releases) {
        for (;;) {
            //目的就是把state加1
            int current = getState();
            int next = current + releases;
            if (next < current) // overflow
                throw new Error("Maximum permit count exceeded");
            if (compareAndSetState(current, next))
                return true;
        }
    }

               doReleaseShared  执行

private void doReleaseShared() {
    for (;;) {
        Node h = head;
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) {
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;            // loop to recheck cases
                //唤醒队列中park的线程    
                unparkSuccessor(h);
            }
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;                // loop on failed CAS
        }
        if (h == head)                   // loop if head changed
            break;
    }
}

             没有获取到位子的线程  ,重新开始自旋获取位子

              如果锁(本人通俗叫做位子)竞争激烈,在非公平的情况下,当前线程有可能重新被挂起。

              生活场景:读书的时候临考期间,很多人都会去图书馆占座,因为图书馆的位子是固定的,只有别人走了,你才能坐,你在没有位子的时候处于等待状态,这是如果有人起身离开,如果在公平的情况下,你就会拥有这个宝贵的座位,但是总有些人没有注意到你的存在,或者就是不守规矩,他本来再你后面来的就可能抢先一步拿到座位,这就是非公平,类比上面的Semaphore,就很容易理解它的原理了。

posted @ 2019-07-24 14:55  Don'tYouSee  阅读(226)  评论(0)    收藏  举报