Semaphore原理分析

      计数信号量用来控制同时访问某个特定资源的操作数量,或者同时执行某个指定操作的数量。计数信号量还可以用来实现某种资源池,或者对容器施加边界,如数据库连接池,固定大小的数据集。
      Semaphore 管理一组虚拟的许可,在执行操作的时可以先获得许可,并在使用以后释放许可。如果没有许可,那么 acquire 将阻塞直到有许可,release 方法返回一个可用信号量。Semaphore 内部是通过 AQS 来实现的。

Semaphore 主要实现方法:

  1. Semaphore 构造函数,传入信号数目,Semaphore 将信号数目设置到 AQS 的 state 上;
  2. acquire 获取信号,如果信号数目大于0,通过 CAS 将信号数减1, 获取成功;否则被阻塞,并将线程记录到 AQS 的等待锁队列中;
  3. release 释放信号,通过 CAS 将信号数加1,并唤醒等待队列上队首线程重新尝试获取信号。


Semapore 结构:

public class Semaphore implements Serializable {
    private static final long serialVersionUID = -3222578661600680210L;
    private final Semaphore.Sync sync;

    public Semaphore(int var1) {
        this.sync = new Semaphore.NonfairSync(var1);
    }

    public Semaphore(int var1, boolean var2) {
        this.sync = (Semaphore.Sync)(var2?new Semaphore.FairSync(var1):new Semaphore.NonfairSync(var1));
    }

    public void acquire() throws InterruptedException {
        this.sync.acquireSharedInterruptibly(1);
    }

    public void acquireUninterruptibly() {
        this.sync.acquireShared(1);
    }

    public boolean tryAcquire() {
        return this.sync.nonfairTryAcquireShared(1) >= 0;
    }

    public boolean tryAcquire(long var1, TimeUnit var3) throws InterruptedException {
        return this.sync.tryAcquireSharedNanos(1, var3.toNanos(var1));
    }

    public void release() {
        this.sync.releaseShared(1);
    }

    public void acquire(int var1) throws InterruptedException {
        if(var1 < 0) {
            throw new IllegalArgumentException();
        } else {
            this.sync.acquireSharedInterruptibly(var1);
        }
    }

    public void acquireUninterruptibly(int var1) {
        if(var1 < 0) {
            throw new IllegalArgumentException();
        } else {
            this.sync.acquireShared(var1);
        }
    }

    public boolean tryAcquire(int var1) {
        if(var1 < 0) {
            throw new IllegalArgumentException();
        } else {
            return this.sync.nonfairTryAcquireShared(var1) >= 0;
        }
    }

    public boolean tryAcquire(int var1, long var2, TimeUnit var4) throws InterruptedException {
        if(var1 < 0) {
            throw new IllegalArgumentException();
        } else {
            return this.sync.tryAcquireSharedNanos(var1, var4.toNanos(var2));
        }
    }

    public void release(int var1) {
        if(var1 < 0) {
            throw new IllegalArgumentException();
        } else {
            this.sync.releaseShared(var1);
        }
    }   
 }

从 Semaphore 中我们可以看到 Semaphore 的锁是通过 Sync 这个类完成的,Sync 则继承自 AQS ,AQS 是独占锁和共享锁的父类,通过继承 AQS 实现共享锁。
Sync 有两个子类是 FairSync 和 NonfairSync,分别代表公平锁和非公平锁。Semaphore 默认是非公平锁。为什么使用非公平锁,这个是性能上的考虑,如果每次都去唤醒线程去获取信号,这是非常消耗资源的,非公平锁的性能和吞吐量也明显优于公平锁。

FairSync 结构:

    static final class FairSync extends Semaphore.Sync {
        private static final long serialVersionUID = 2014338818796000944L;

        FairSync(int var1) {
            super(var1);
        }

        protected int tryAcquireShared(int var1) {
            int var2;
            int var3;
            do {
                if(this.hasQueuedPredecessors()) {
                    return -1;
                }

                var2 = this.getState();
                var3 = var2 - var1;
            } while(var3 >= 0 && !this.compareAndSetState(var2, var3));

            return var3;
        }
    }

FairSync 判断是否有线程等待,如果没有则尝试获取信号,如果有则加入到等待队列。

NonfariSync 结构:

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

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

        protected int tryAcquireShared(int var1) {
            return this.nonfairTryAcquireShared(var1);
        }
    }

    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 1192457210091910933L;

        Sync(int var1) {
            this.setState(var1);
        }

        final int getPermits() {
            return this.getState();
        }

        final int nonfairTryAcquireShared(int var1) {
            int var2;
            int var3;
            do {
                var2 = this.getState();
                var3 = var2 - var1;
            } while(var3 >= 0 && !this.compareAndSetState(var2, var3));

            return var3;
        }

        protected final boolean tryReleaseShared(int var1) {
            int var2;
            int var3;
            do {
                var2 = this.getState();
                var3 = var2 + var1;
                if(var3 < var2) {
                    throw new Error("Maximum permit count exceeded");
                }
            } while(!this.compareAndSetState(var2, var3));

            return true;
        }

        final void reducePermits(int var1) {
            int var2;
            int var3;
            do {
                var2 = this.getState();
                var3 = var2 - var1;
                if(var3 > var2) {
                    throw new Error("Permit count underflow");
                }
            } while(!this.compareAndSetState(var2, var3));

        }

        final int drainPermits() {
            int var1;
            do {
                var1 = this.getState();
            } while(var1 != 0 && !this.compareAndSetState(var1, 0));

            return var1;
        }
    }

NonfairSync 则先尝试获取锁,如果获取失败则再加入等待队列。

AQS 结构:

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements Serializable {
    private static final long serialVersionUID = 7373984972572414691L;
    private transient volatile AbstractQueuedSynchronizer.Node head;
    private transient volatile AbstractQueuedSynchronizer.Node tail;
    private volatile int state;
    static final long spinForTimeoutThreshold = 1000L;
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long stateOffset;
    private static final long headOffset;
    private static final long tailOffset;
    private static final long waitStatusOffset;
    private static final long nextOffset;

    protected AbstractQueuedSynchronizer() {
    }
	
   public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }
    // 获取失败会被阻塞
    private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    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);
        }
    }

    // 唤醒等待线程队首线程
    private void doReleaseShared() {
        /*
         * Ensure that a release propagates, even if there are other
         * in-progress acquires/releases.  This proceeds in the usual
         * way of trying to unparkSuccessor of head if it needs
         * signal. But if it does not, status is set to PROPAGATE to
         * ensure that upon release, propagation continues.
         * Additionally, we must loop in case a new node is added
         * while we are doing this. Also, unlike other uses of
         * unparkSuccessor, we need to know if CAS to reset status
         * fails, if so rechecking.
         */
        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
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }

    private void doReleaseShared() {
        while(true) {
            AbstractQueuedSynchronizer.Node var1 = this.head;
            if(var1 != null && var1 != this.tail) {
                int var2 = var1.waitStatus;
                if(var2 == -1) {
                    if(!compareAndSetWaitStatus(var1, -1, 0)) {
                        continue;
                    }

                    this.unparkSuccessor(var1);
                } else if(var2 == 0 && !compareAndSetWaitStatus(var1, 0, -3)) {
                    continue;
                }
            }

            if(var1 == this.head) {
                return;
            }
        }
    }
}

Semaphore 调用 acquire方法时,调用 AQS 的 acquireSharedInterruptibly 方法,AQS 则调用子类的 tryAcquireShared 方法,如果获取成功,则直接返回;如果获取失败,则调用调用 AQS 的方法 doAcquireSharedInterruptibly 阻塞并加入到等待队列等待唤醒。
Semaphore 调用 release 方法时,调用 AQS 的 releaseShared 方法,AQS 则调用子类的 tryReleaseShared 方法,如果释放成功,则调用 doReleaseShared 方法唤醒等待队列队首线程,线程启动后,如果 tryAcquireShared 返回值大于等于 0,则通过 setHeadAndPropagate 方法进行传播,唤醒下一个线程。

转载于:https://my.oschina.net/u/2950586/blog/1507499

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值