JAVA并发之Semaphore(信号量)(java的并发机制是什么)
前面几篇文章我们讲了可重入锁和读写锁(见文末链接),本篇文章主要讲下Java并发包下面另一个工具类Semaphore(信号量)的原理。
Semaphore特点
- 是一种共享锁(类似于读写锁中的读锁)
- 基于AQS实现(AQS相关内容可以参考文末链接)
- 允许一定数量的线程获得锁
Semaphore例子
JDK源码对Semaphore的介绍是可以用来限制一定数量的线程访问某个资源,下面我们通过一个例子来看下具体是什么意思。
public static void main(String[] args) {
DateFormat df = new SimpleDateFormat("HH:mm:ss---");
Semaphore semaphore = new Semaphore(3);
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();
System.out.println(df.format(new Date()) + Thread.currentThread().getName() + " got resource");
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}
};
for (int i = 0; i < 9; i++) {
new Thread(runnable, "Thread" + i).start();
}
}
上例中,我们创建了一个限制是3的信号量,同时启动了9个线程去执行各自的任务(这里都休眠了2秒钟),从下面打印的结果我们可以看到,同时只有3个线程能够获得信号量并执行。
11:12:29---Thread1 got resource
11:12:29---Thread3 got resource
11:12:29---Thread0 got resource
11:12:31---Thread4 got resource
11:12:31---Thread2 got resource
11:12:31---Thread6 got resource
11:12:33---Thread5 got resource
11:12:33---Thread7 got resource
11:12:33---Thread8 got resource
Semaphore原理
Semaphore内部包含了一个Sync对象继承了AQS,而Sync对象又有两个子类NonfaireSync和FairSync,因此Semaphore也支持公平锁和非公平锁两个功能。
同时,公平锁和非公平锁也体现到了Semaphore的构造函数上面:
//默认非公平锁
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
//fair为true时表示公平锁
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
获取Semaphore锁的方法如下,其主要方法有两个:
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
tryAcquireShared:尝试去获取锁,获取成功则返回正数或0,获取失败返回负数。
这里公平锁和非公平锁有不同的实现逻辑:
公平锁:前线程检查如果AQS里面有线程在排队了,则当返回-1,进入AQS排队;否则和其他线程竞争获取锁,竞争成功则获取锁返回,竞争失败则进入AQS排队。
protected int tryAcquireShared(int acquires) {
for (;;) {
if (hasQueuedPredecessors())
return -1;
// 通过state来做线程数量限制
// state的值和构造函数里面的permits参数是一个值
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
非公平锁:当前线程直接参与获取锁的竞争,竞争成功则获取锁返回,竞争失败则进入AQS排队(不考虑是否AQS已经有其他线程在排队)。
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
doAcquireSharedInterruptibly:类似于读锁调用的doAcquireShared方法,如果当前线程获取到锁,则唤醒它在AQS的后继结点,否则进入AQS排队。
释放Semaphore锁比较简单,公平锁和非公平锁都是一样的逻辑:
将state数值加1,并且唤醒正在AQS排队的其他线程。
Demo代码位置
src/main/java/net/weichitech/juc/SemaphoreTest.java · 小西学编程/java-learning - Gitee.com