【并发】Semaphore

使用概述

Semaphore是用来控制同时访问共享资源的线程数量,有流量控制的作用。

Semaphore维护一批许可,线程通过调用acquire方法来获取许可,获取不到则会发生阻塞,获取到的线程执行完自身逻辑后需要调用release方法进行释放。

经典使用场景如下:线程从资源池中获取资源时需要先获取许可,释放资源时也需要释放许可,以便后续的线程可以获得许可。构造函数的第2个参数指定是否采用公平策略,公平策略侧重于保证每个线程不会发生饥饿,而非公平策略侧重于提升吞吐量。获取资源类的需求一般倾向于使用公平策略。

public class Pool {
    private static final int MAX_AVAILABLE = 100;
    private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);

    protected Object[] items = new Object[MAX_AVAILABLE];
    protected boolean[] used = new boolean[MAX_AVAILABLE];

    public Object getItem() throws InterruptedException {
        available.acquire();
        return getNextAvailableItem();
    }

    public void putItem(Object x) {
        if (markAsUnused(x)) {
            available.release();
        }
    }

    protected synchronized Object getNextAvailableItem() {
        for (int i = 0; i < MAX_AVAILABLE; ++i) {
            if (!used[i]) {
                used[i] = true;
                return items[i];
            }
        }
        return null; // not reached
    }

    protected synchronized boolean markAsUnused(Object item) {
        for (int i = 0; i < MAX_AVAILABLE; ++i) {
            if (item == items[i]) {
                if (used[i]) {
                    used[i] = false;
                    return true;
                } else
                    return false;
            }
        }
        return false;
    }
}

源码分析

(1) 数据结构
//同步器,AQS的子类
private final Sync sync;

Sync为Semaphore内部的抽象类,继承AQS,通过同步队列来实现线程的调度。

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

    //permits其实就是AQS中的共享变量state
    Sync(int permits) {
        setState(permits);
    }

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

    //返回剩余许可数
    final int nonfairTryAcquireShared(int acquires) {
        for (;;) {
            int available = getState();
            int remaining = available - acquires;
            if (remaining < 0 ||
                compareAndSetState(available, remaining))
                return remaining;
        }
    }

    //释放许可,即将state加上释放许可数
    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");
            if (compareAndSetState(current, next))
                return true;
        }
    }

    final void reducePermits(int reductions) {
        for (;;) {
            int current = getState();
            int next = current - reductions;
            if (next > current) // underflow
                throw new Error("Permit count underflow");
            if (compareAndSetState(current, next))
                return;
        }
    }

    final int drainPermits() {
        for (;;) {
            int current = getState();
            if (current == 0 || compareAndSetState(current, 0))
                return current;
        }
    }
}


Semaphore内部的Sync有两种实现:

  • 公平同步器:严格按照同步队列的顺序获取许可,保证不会出现线程饥饿。
  • 非公平同步器:新的线程在调用acquire方法时,会立即尝试获取许可,获取不到才会进入同步队列,可能会发生线程饥饿的情况,但吞吐量较高,这也是默认的同步器。
/**
 * NonFair version
 */
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);
    }
}

/**
 * Fair version
 */
static final class FairSync extends Sync {
    private static final long serialVersionUID = 2014338818796000944L;

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

    //公平策略仅多出判定当前线程是否是同步队列的可执行节点
    protected int tryAcquireShared(int acquires) {
        for (;;) {
            if (hasQueuedPredecessors())
                return -1;
            int available = getState();
            int remaining = available - acquires;
            if (remaining < 0 ||
                compareAndSetState(available, remaining))
                return remaining;
        }
    }
}
(2) 常用方法
构造函数
//默认非公平同步器策略
public Semaphore(int permits) {
    sync = new NonfairSync(permits);
}

//通过fair参数指定同步器
public Semaphore(int permits, boolean fair) {
    sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
acquire()

acquire()调用AQS的acquireSharedInterruptibly(int arg)方法,可响应中断。

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

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

tryAcquireShared(int acquires)会调用Semaohore内部的FairSync或者NonfairSync相应的实现,其返回剩余的许可数量,区别在于FairSync会判定当前线程是否是同步队列中的可执行结点,而NonfairSync不用。

如果剩余的许可数量不足,则会执行AQS的doAcquireSharedInterruptibly(int arg)方法,该方法主要将当前线程以共享结点的方式加入到同步队列的尾部,然后就是在同步队列中等待被唤醒。

//class: AbstractQueuedSynchronizer
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);
    }
}
tryAcquire()

tryAcquire()只会尝试获取一次,无论成败都立即返回,不会进入同步队列进行等待。

//nonfairTryAcquireShared主要通过CAS更新state的值,并返回剩余的许可数,
//许可数小于0则表示获取失败,大于等于0表示获取成功
public boolean tryAcquire() {
    return sync.nonfairTryAcquireShared(1) >= 0;
}
tryAcquire(long timeout, TimeUnit unit)

tryAcquire(long timeout, TimeUnit unit)方法回立即尝试获取许可,获取失败会进入同步队列等待,只是如果超时会返回。

该方法调用AQS的tryAcquireSharedNanos(int arg, long nanosTimeout)方法,同样可响应中断

public boolean tryAcquire(long timeout, TimeUnit unit)
    throws InterruptedException {
    return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}

//class: AbstractQueuedSynchronizer
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    return tryAcquireShared(arg) >= 0 ||
        doAcquireSharedNanos(arg, nanosTimeout);
}

tryAcquireSharedNanos(int arg, long nanosTimeout)方法会先立即尝试获取许可,获取失败则调用doAcquireSharedNanos(arg, nanosTimeout)方法,该方法与上述分析的doAcquireSharedInterruptibly(int arg)方法类似,只是多了一步超时直接返回的逻辑。

private boolean doAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException {
    if (nanosTimeout <= 0L)
        return false;
    final long deadline = System.nanoTime() + nanosTimeout;
    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 true;
                }
            }
            nanosTimeout = deadline - System.nanoTime();
            //如果超时则立即返回
            if (nanosTimeout <= 0L)
                return false;
            //spinForTimeoutThreshold为自旋时间,默认为1000纳秒
            if (shouldParkAfterFailedAcquire(p, node) &&
                nanosTimeout > spinForTimeoutThreshold)
                LockSupport.parkNanos(this, nanosTimeout);
            if (Thread.interrupted())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
release()

release()的作用是释放1个许可。

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

//class: AbstractQueuedSynchronizer
public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}

tryReleaseShared(int releases)调用Semaphore的内部类Sync的实现,逻辑其实很简单,就是把releases加回到共享变量state。释放许可后,调用AQS的doReleaseShared()方法唤醒后继结点。

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");
        if (compareAndSetState(current, next))
            return true;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值