【源码】JUC —— Semaphore 浅析

前言

Semaphore ,翻译:信号量,跟 ReentrantLock 相对的,共享锁 的经典实现,基于 AQS ,也提供了 公平锁非公平锁 两种机制

关于 ReentrantLock ,可阅读

【源码】JUC —— ReentrantLock 浅析

JDK 版本

JDK11

Sync

内部类 Sync 继承 AQS,实现相关方法

		Sync(int permits) {
            setState(permits);
        }

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

构造时可以初始化 信号量 的个数,即最多同时被多少线程 共享

nonfairTryAcquireShared

		final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();
                int remaining = available - acquires;

                /**
                 * 没有剩余资源则直接返回 负数
                 * 剩余资源 >= 0,则 CAS 修改 信号量 个数,修改成功则返回
                 */
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }

信号量 获取,如果剩余 信号量 不满足需求,则直接返回一个负数

如果 信号量 足够,则尝试 CAS 修改 state,修改成功的线程即成功获取到 信号量,返回剩余 信号量 个数,其余线程继续 自旋 尝试

此处 自旋 即不停地 for循环

tryReleaseShared

		protected final boolean tryReleaseShared(int releases) {
            for (;;) {
                int current = getState();
                int next = current + releases;
                // 溢出
                if (next < current)
                    throw new Error("Maximum permit count exceeded");

                // CAS 修改 信号量 个数
                if (compareAndSetState(current, next))
                    return true;
            }
        }

释放 信号量,在校验没有溢出后,CAS 修改 信号量 个数

其他方法

		// 减少 信号量
        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 也有 公平锁非公平锁 版本

NonfairSync

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

非公平 实现,tryAcquireShared 委托给 nonfairTryAcquireShared 方法

FairSync

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

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

        protected int tryAcquireShared(int acquires) {
            for (;;) {
                // 如果有节点排队,直接返回 -1
                if (hasQueuedPredecessors())
                    return -1;
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }
    }

公平 实现,会在试图获取 信号量 之前,先判断是否有节点在排队,有则直接返回 -1 表示获取 信号量 失败

构造

	public Semaphore(int permits) {
        sync = new NonfairSync(permits);
    }

    public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }

默认构造 非公平 实现,且允许传入 permits 参数初始化 信号量 个数,fair 为 true,则构造 公平 实现

方法实现(委托)

	// 获取 信号量(1个),响应中断
	public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

	// 获取 信号量(1个),不响应中断
	public void acquireUninterruptibly() {
        sync.acquireShared(1);
    }

	// 获取信号量,是否成功
	public boolean tryAcquire() {
        return sync.nonfairTryAcquireShared(1) >= 0;
    }

	// 限时获取,超时则失败
	public boolean tryAcquire(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }

	// 释放 信号量(1个)
	public void release() {
        sync.releaseShared(1);
    }

	// 获取 permits 个信号量,响应中断
	public void acquire(int permits) throws InterruptedException {
        if (permits < 0) throw new IllegalArgumentException();
        sync.acquireSharedInterruptibly(permits);
    }

	// 获取 permits 个信号量,不响应中断
	public void acquireUninterruptibly(int permits) {
        if (permits < 0) throw new IllegalArgumentException();
        sync.acquireShared(permits);
    }

	// 获取 permits 个信号量是否成功
	public boolean tryAcquire(int permits) {
        if (permits < 0) throw new IllegalArgumentException();
        return sync.nonfairTryAcquireShared(permits) >= 0;
    }

	// 限时获取 permits 个信号量,超时则失败
	public boolean tryAcquire(int permits, long timeout, TimeUnit unit)
        throws InterruptedException {
        if (permits < 0) throw new IllegalArgumentException();
        return sync.tryAcquireSharedNanos(permits, unit.toNanos(timeout));
    }

	// 释放 permits 个信号量
	public void release(int permits) {
        if (permits < 0) throw new IllegalArgumentException();
        sync.releaseShared(permits);
    }

	// 剩余 信号量 个数
	public int availablePermits() {
        return sync.getPermits();
    }

	// 略

可以发现,不像 ReentrantLock 提供的 lockunlock 方法,Semaphore信号量 获取释放则直接调用 acquirerelease 方法实现

demo

public class SemaphoreTest {

    static Semaphore semaphore = new Semaphore(2, true);

    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                try {
                    test();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }, "t" + i).start();
        }
    }

    static void test() throws InterruptedException {
        semaphore.acquire();
        System.out.println(Thread.currentThread().getName());

        TimeUnit.SECONDS.sleep(3);
        semaphore.release();
    }
}

结果:
t0
t1
------- 停顿 3--------
t2

只初始化了 2 个 信号量,因此 t0 t1 线程可以获取到,待处理完逻辑 release 信号量后, t2 线程才能获取到 信号量

总结

共享锁 的经典实现,好用且有效

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值