AQS源码解读(九)——Semaphore信号量原理详解

最后总结

ActiveMQ+Kafka+RabbitMQ学习笔记PDF

image.png

  • RabbitMQ实战指南

image.png

  • 手写RocketMQ笔记

image.png

  • 手写“Kafka笔记”

image

关于分布式,限流+缓存+缓存,这三大技术(包含:ZooKeeper+Nginx+MongoDB+memcached+Redis+ActiveMQ+Kafka+RabbitMQ)等等。这些相关的面试也好,还有手写以及学习的笔记PDF,都是啃透分布式技术必不可少的宝藏。以上的每一个专题每一个小分类都有相关的介绍,并且小编也已经将其整理成PDF啦

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

semaphore.release();//释放一个资源许可

10个资源许可被取完后,再来线程获取就会入队列阻塞。

Semaphore是在AQS基础上实现的共享锁,获取资源和释放资源都是调用的AQS中共享锁模板方法,故只需要看tryAcquireSharedtryReleaseSharedSemaphore中的实现。

代码示例


首先初始化Semaphore,给资源许可5个,模拟多个线程获取资源。

public class Test1072 {

private static Semaphore semaphore = new Semaphore(5);

public static void main(String[] args) {

for (int i = 0; i < 10; i++) {

Thread thread = new Thread(new MyThread(i));

thread.start();

}

}

static class MyThread implements Runnable{

private int i;

public MyThread(int i) {

this.i = i;

}

@Override

public void run() {

try {

semaphore.acquire();

System.out.println(Thread.currentThread().getName() + “, semaphore.acquire()获取资源–” + i);

} catch (InterruptedException e) {

e.printStackTrace();

} finally {

// System.out.println(Thread.currentThread().getName() + “,semaphore.release()释放资源–” + i);

// semaphore.release();

}

}

}

}

//控制台输出

//线程只获取资源不释放

Thread-0, semaphore.acquire()获取资源–0

Thread-1, semaphore.acquire()获取资源–1

Thread-2, semaphore.acquire()获取资源–2

Thread-6, semaphore.acquire()获取资源–6

Thread-3, semaphore.acquire()获取资源–3

//线程获取资源并释放资源

Thread-0, semaphore.acquire()获取资源–0

Thread-2, semaphore.acquire()获取资源–2

Thread-2,semaphore.release()释放资源–2

Thread-1, semaphore.acquire()获取资源–1

Thread-1,semaphore.release()释放资源–1

Thread-0,semaphore.release()释放资源–0

Thread-3, semaphore.acquire()获取资源–3

Thread-3,semaphore.release()释放资源–3

Thread-5, semaphore.acquire()获取资源–5

Thread-5,semaphore.release()释放资源–5

Thread-4, semaphore.acquire()获取资源–4

Thread-4,semaphore.release()释放资源–4

Thread-6, semaphore.acquire()获取资源–6

Thread-6,semaphore.release()释放资源–6

Thread-7, semaphore.acquire()获取资源–7

Thread-7,semaphore.release()释放资源–7

Thread-9, semaphore.acquire()获取资源–9

Thread-9,semaphore.release()释放资源–9

Thread-8, semaphore.acquire()获取资源–8

Thread-8,semaphore.release()释放资源–8

在没有释放资源前,只有五个线程可以获取资源,其他线程都进入队列阻塞。当线程获取资源,执行完业务代码释放资源,则每个线程都可以获取资源,无需阻塞。

Semaphore基本结构


Semaphore中也有一个内部类SyncSync继承自AbstractQueuedSynchronizer,所以核心代码都在Sync中。从构造函数看出,Sync还有两个子类NonfairSyncFairSync,说明Semaphore实现的共享锁区分公平性。

public class Semaphore implements java.io.Serializable {

private static final long serialVersionUID = -3222578661600680210L;

/** All mechanics via AbstractQueuedSynchronizer subclass */

private final Sync sync;

/**

  • Synchronization implementation for semaphore. Uses AQS state

  • to represent permits. Subclassed into fair and nonfair

  • versions.

*/

abstract static class Sync extends AbstractQueuedSynchronizer {

… …

}

public Semaphore(int permits) {

sync = new NonfairSync(permits);

}

public Semaphore(int permits, boolean fair) {

sync = fair ? new FairSync(permits) : new NonfairSync(permits);

}

}

获取资源许可


Semaphore提供了可中断获取资源许可(Semaphore#acquire()),不可中断获取资源许可(Semaphore#acquireUninterruptibly()),尝试获取资源许可(Semaphore#tryAcquire()),超时可中断获取资源许可(Semaphore#tryAcquire(long, java.util.concurrent.TimeUnit))。底层代码调用的都是AQS中共享模式获取锁的模板方法,故只需要看看tryAcquireSharedSemaphore中的实现。

//1.可中断获取资源许可

public void acquire() throws InterruptedException {

sync.acquireSharedInterruptibly(1);

}

//2.可中断获取permits个资源许可

public void acquire(int permits) throws InterruptedException {

if (permits < 0) throw new IllegalArgumentException();

sync.acquireSharedInterruptibly(permits);

}

//3.不可中断获取资源许可

public void acquireUninterruptibly() {

sync.acquireShared(1);

}

//4.不可中断获取permits个资源许可

public void acquireUninterruptibly(int permits) {

if (permits < 0) throw new IllegalArgumentException();

sync.acquireShared(permits);

}

//5.尝试获取资源许可,获取成功返回true

public boolean tryAcquire() {

return sync.nonfairTryAcquireShared(1) >= 0;

}

//6.尝试获取permits个资源许可,获取成功返回true

public boolean tryAcquire(int permits) {

if (permits < 0) throw new IllegalArgumentException();

return sync.nonfairTryAcquireShared(permits) >= 0;

}

//7.超时可中断获取资源许可,获取失败进入队列阻塞一段时间,超时还未获取返回false

public boolean tryAcquire(long timeout, TimeUnit unit)

throws InterruptedException {

return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));

}

//8.超时可中断获取permits个资源许可,获取失败进入队列阻塞一段时间,超时还未获取返回false

public boolean tryAcquire(int permits, long timeout, TimeUnit unit)

throws InterruptedException {

if (permits < 0) throw new IllegalArgumentException();

return sync.tryAcquireSharedNanos(permits, unit.toNanos(timeout));

}

tryAcquireShared

Semaphore中获取资源许可有公平锁和非公平锁之分。

Semaphore.NonfairSync#tryAcquireShared

NonfairSync中的tryAcquireShared调用了Semaphore.Sync中已经实现好的nonfairTryAcquireShared

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

}

}

获取资源许可,就是对state做减法,remaining =当前许可量—获取的许可量,remaining >= 0即可以CAS修改state资源许可数,获取成功返回remaining资源许可剩余量。CAS修改state失败会不断自旋。

//java.util.concurrent.Semaphore.Sync#nonfairTryAcquireShared

final int nonfairTryAcquireShared(int acquires) {

for (;😉 {

//获取可用资源许可量

int available = getState();

//计算可用资源许可剩余量

int remaining = available - acquires;

//remaining <0 说明资源许可耗尽

//remaining >= 0 可继续获取资源许可cas

if (remaining < 0 ||

compareAndSetState(available, remaining))

//返回剩余许可量

return remaining;

}

}

Semaphore.FairSync#tryAcquireShared

FairSynctryAcquireSharedNonfairSync唯一的区别就是FairSync中首先会判断当前同步队列中是否有线程也在等待获取资源许可,有则获取失败返回-1,没有则继续获取许可。CAS修改state失败会不断自旋。

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;

}

}

}

最后

各位读者,由于本篇幅度过长,为了避免影响阅读体验,下面我就大概概括了整理了

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

|

compareAndSetState(available, remaining))

return remaining;

}

}

}

最后

各位读者,由于本篇幅度过长,为了避免影响阅读体验,下面我就大概概括了整理了

[外链图片转存中…(img-6CytMWL4-1715811226174)]

[外链图片转存中…(img-4J6pBP8V-1715811226175)]

[外链图片转存中…(img-MpiG3mUE-1715811226175)]

[外链图片转存中…(img-YLTIuK2D-1715811226175)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值