Java并发编程|第九篇:Semaphore 信号量
文章目录
系列文章
1.介绍
semaphore也可以说是信号量,可以控制线程访问的数量,它有两个方法,第一acquire()获得一个许可,如果没有就等待,第二release()释放一个许可;有点类似限流的功能。
2.例子
这个例子可以理解为10辆车,去抢5个车位
/**
* 限流
* 1.设置线程个数new Semaphore(5)
* 2.semaphore.acquire();线程获取lock
* 3.semaphore.release();线程释放lock
* 4.当线程获取5个锁之后,其他线程阻塞
* */
public class SemaphoreDemo {
static class Car extends Thread {
private int num;
private Semaphore semaphore;
public Car(int num, Semaphore semaphore) {
this.num = num;
this.semaphore = semaphore;
}
@Override
public void run() {
try {
//抢占车位
semaphore.acquire();
System.out.println("第"+num+"抢占车位");
Thread.sleep(100);//停车100ms
semaphore.release();//车开走
System.out.println("第"+num+"释放");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
//设置5个车位
Semaphore semaphore = new Semaphore(5);
for (int i = 0; i < 10; i++) {
new Car(i, semaphore).start();
}
}
}
执行结果:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DwDqvb8p-1576741771200)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images)]](https://i-blog.csdnimg.cn/blog_migrate/acb1de5b748f434e0057999547b9b131.png)
3.源码分析
以停车的代码为例
构造方法
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
最后调用的是Sync()构造,设置 setState 设置 state 值 为 5 ,有5个车位
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 1192457210091910933L;
Sync(int permits) {
setState(permits);
}
...此处省略N行代码
acquire()
这个方法和
CoutDownLatch里面调用的方法是一样的
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
acquireSharedInterruptibly()
将线程加入到
AQS共享锁队列
tryAcquireShared(arg)<0表示没有可用的线程数量,执行doAcquireSharedInterruptibly()将线程加入AQS共享锁队列
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)//没有可用的线程数量
doAcquireSharedInterruptibly(arg);
}
后面的方法就不做展开了可以参考
对应的方法即可
区别在于其中几个方法实现方式
Sync类
Sync这个类在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);
}
}
nonfairTryAcquireShared()
1.先获取目前的可用数量
state值,比如说第一次为 5 个车位2.然后 remaining = 5 - 1 = 4
3.判断 remining < 0 返回负值,表示没有可用的车位
4.CAS将
available值替换为 4 如果成功,返回 4,表示有 4 个车位可以停车
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
公平FairSync
和非公平的区别就是,在执行之前先
hasQueuedPredecessors()判断一下,是否有线程在排队
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();//获得当前的state值
int remaining = available - acquires;//可用的数量 - 已经获得锁的数量
if (remaining < 0 ||
compareAndSetState(available, remaining))//CAS 将可用数量替换为remaining
return remaining;
}
}
}
release()
释放,也是和
CoutDownLatch中的countDown()完全一样,参照之前的文章即可
public void release() {
sync.releaseShared(1);
}
tryReleaseShared()
这个方法是
Semaphore自己的实现
通过tryReleaseShared()可以增加1个可用线程数,及当前state+ 1
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();//获得当前可以用的线程数
int next = current + releases;//可以用线程数+1
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next))//修改当前的可用线程数+1
return true;
}
}
总结
整个的实现原理和CoutDownLatch 非常相似,都是采用AQS的共享锁实现。
区别在于
第一:加入共享锁队列的条件不一样,当tryAcquireShared()<0时,表示没有可用线程数量,此时将线程加入到AQS的共享锁队列
第二:唤醒线程节点的条件也不一样,tryAcquireShared()>0,表示可用线程数 > 0,可用线程数量增加(通过tryReleaseShared()增加 1 个可用线程数),线程在去竞争锁,执行程序
4.参考
腾讯课堂->咕泡学院->mic老师->并发编程基础
本文深入解析Java中的Semaphore信号量,一种用于控制多个线程访问共享资源的同步工具。通过实例演示了Semaphore的基本使用,包括acquire和release方法,并对比了其与CountDownLatch的异同。此外,还详细分析了Semaphore的源码实现,包括非公平和公平两种模式的区别。
170万+

被折叠的 条评论
为什么被折叠?



