Semaphore(一)

Semaphore(一)

了解Semaphore

Semaphore是什么

semaphore是JUC包里的一个同步工具类,翻译成中文就是信号量,一个计数信号量。以下都是从文档中的理解:

概念上,Semaphore维护了一组许可证,在许可证可用之前阻塞acquire()方法;而许可证有可用之后,获取该许可证,立即从acquire()方法返回。而release()就使得有可用的许可证的方法,每个release()添加一个许可证,从而会释放一个正在阻塞等待acquire()的获取线程。

事实上,Semaphore里面并没有什么许可证对象。许可证只是我们在new一个Semaphore出来时,设置进去的int类型的数据(permit)

Semaphore通常用于限制访问某些资源的线程数目

该Semaphore在new的时候还有一个参数: boolean fair。该参数就是设置是否公平。

  • 若fair为false,表示使用不公平的策略(在源码中表现为使用NonfairSync,这个是什么待会详细说说)。不公平策略意味着你在排队的时候,可能会被人插队,看起来非常不公平。同理一个线程在等待获取许可证,但是在你之后的线程却已经被调到队头,获取了许可证。
  • 若fair为true,表示使用公平策略(在源码中表现为使用FairSync)。公平策略就是遵守FIFO(first-in, first-out)。在文档里有这样一个注意事项:Note that FIFO ordering necessarily applies to specific internal points of execution within these methods. So, it is possible for one thread to invoke {@code acquire} before another, but reach the ordering point after the other, and similarly upon return from the method.也就是说**FIFO会作用于在这些方法内(指的是用户的方法)的特定内部执行点,所以完全有可能就是一个线程A比另一个线程B更早地调用acquire()方法,但是这还没完,线程B知道自己一开始落后于别人,就努力争取比别人排队排的早一点,前一点。这样就出现了比线程B更早获取acquire()的A,排在了B后面,最后B比A更早获取一个许可证。

哦对了,tryAcquire()中无论你设置了什么样的策略,他都不会遵守,它是只要有可用的,就给你,体现了try嘛,随便试一下

源码浅析

由于Semaphore里面有比较多的方法,因此不再按以前的排版了(官方吐槽,以前排版的确看着有点乱2333333333333)

构造方法

1
2
3
4
5
6
7
8
9
10
// 如果不指定fair,则默认使用NonfairSync
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}


public Semaphore(int permits, boolean fair) {
// 这里首先判断fair是true还是false,如果是true,则sync是FairSync,如果是false,则sync是NonfairSync。
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

上面构造方法中,不指定fair参数,默认使用不公平策略(即抢占式),当有也可以指定使用公平策略。那么在构造方法中就引出了FairSync和NonfairSync了,我们来看看

  • FairSync
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// FairSync	
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;
}
}
}

继承了Sync,重写了tryAcquireShared。tryAcquireShared不是Sync类的方法,而是Sync继承自AQS的方法,AQS里没有具体实现,让其子类实现。

  • NonfairSync
1
2
3
4
5
6
7
8
9
10
11
12
// 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);
}
}
1
2
3
4
5
6
7
8
9
10
// 上面NonfairSync中tryAcquireShared的nonfairTryAcquireShared方法,该方法在Sync中。
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}

NonfairSync也是重写了AQS的tryAcquireShared方法。

  • FairSync与NonfairSync的比较
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// FairSync
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;
}
}
// NonfairSync
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}

从上面可以直观的看到FairSync与NonfairSync的区别。虽然就只有两行代码的区别,但是这两行意义不一样。FairSync之所以称为公平,就是会检查当前队列中,在你之前有没有别的线程在等待,如果有,说明你不是第一,不好意思,麻烦请排队,如果没有,说明你是第一,ok,这就从可用的给你你需要的许可证数量。而NonfairSync就是没有检查,直接给你你需要的许可证。

未完待续…

-------------本文结束感谢您的阅读-------------