
person-walking-on-pathway-3348363.jpg
简介:
多线程一直是Java中的一个难点和重点,里面涉及到的东西很多,今天我们要介绍的就是Semaphore(信号量),可能很多人对它不太熟悉,用到的概率不是很大。不过,在Java的世界里,任何对象的存在,都有它独特的意义。首先,这个类也是在java.util.concurrent这个并发包下面的,也是用来控制多线程访问的。一般是用来限制访问某一资源的线程数的,不像synchronized一次只允许一个线程访问同一个资源,Semaphore可以灵活的定义访问资源的数量,感觉和令牌桶的算法差不多。
类图:

Semaphore类图.png
举例:
接下来的代码例子其实是Semaphore类注释上面的,拿过来一起学习一下。
private static final int MAX_AVAILABLE = 2;//the initial number of permits available(可同时允许的线程数量)
private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);//true:公平锁
protected Object[] items = {"a","b","c","d","e","f","g"};//资源
protected boolean[] used = new boolean[MAX_AVAILABLE];
public Object getItem() throws InterruptedException {//线程获取资源的入口,在获取资源之前,首先要过acquire这一关
available.acquire();
return getNextAvailableItem();
}
public void putItem(Object x) {//用完之后,归还permits,这样其他的线程,才可以继续用
if (markAsUnused(x))
available.release();
}
protected synchronized Object getNextAvailableItem() {//synchronized修饰的方法,因为有可能会有多个线程,所以要保证线程安全
for (int i = 0; i < MAX_AVAILABLE; ++i) {
if (!used[i]) {
used[i] = true;
return items[i];
}
}
return null;
}
大家可以自己在这个例子基础上测试一下,体会一下其中的思想。
源码:
源码简单分析一下,其实Semaphore的底层依靠的还是强大的AQS,就像上面的类图一样,在AQS的基础上,可以自定义自己的逻辑,比如,把permit放在state里面。
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 1192457210091910933L;
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;
}
}
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;
}
}
}
这个类其实用到了模板模式的思想,把一些公共的方法放在抽象类里面,然后下面一个NonfairSync,一个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;
}
}
}
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);
}
}
总结:
Java里面的知识,说实话需要学习的东西确实非常多,需要花费很多精力。但同时,随着你看源码看的越多,会慢慢的发现,好多东西的设计思想都是类似的,并且写的代码,都不是很复杂,仅仅是一层套一层,把人套糊涂啦。所以在学习知识的时候,就像一位大神说过的,最好先看官方文档,先了解其整体脉络,然后再带着问题去看源码,去寻找答案,这样效率会很高。