您好,登录后才能下订单哦!
在多线程编程中,控制并发访问共享资源是一个常见的问题。Java提供了多种同步工具来帮助开发者解决这些问题,其中Semaphore
是一个非常重要的同步组件。Semaphore
基于AQS(AbstractQueuedSynchronizer)实现,可以用来控制同时访问特定资源的线程数量。本文将详细介绍Semaphore
的使用方法、底层实现、高级用法以及注意事项,帮助读者更好地理解和应用Semaphore
。
Semaphore
(信号量)是一种用于控制多个线程对共享资源访问的同步工具。它可以用来限制同时访问某一资源的线程数量。Semaphore
维护了一个许可集,线程在访问资源之前必须先获取许可,访问结束后释放许可。如果许可集为空,则线程必须等待,直到有其他线程释放许可。
Semaphore
常用于以下场景:
Semaphore
的构造函数有两个主要参数:
permits
:许可的数量,即允许同时访问资源的线程数量。fair
:是否公平,如果为true
,则等待时间最长的线程优先获取许可。Semaphore semaphore = new Semaphore(10, true); // 创建一个有10个许可的公平信号量
线程在访问资源之前需要调用acquire()
方法获取许可。如果许可集为空,则线程进入阻塞状态,直到有许可可用。
semaphore.acquire(); // 获取一个许可
线程在访问资源结束后需要调用release()
方法释放许可,以便其他线程可以获取许可。
semaphore.release(); // 释放一个许可
以下是一个简单的示例,展示了如何使用Semaphore
来控制对共享资源的访问。
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
private static final int THREAD_COUNT = 10;
private static final Semaphore semaphore = new Semaphore(5); // 允许5个线程同时访问
public static void main(String[] args) {
for (int i = 0; i < THREAD_COUNT; i++) {
new Thread(new Worker(i)).start();
}
}
static class Worker implements Runnable {
private final int id;
Worker(int id) {
this.id = id;
}
@Override
public void run() {
try {
semaphore.acquire();
System.out.println("Thread " + id + " is working");
Thread.sleep(2000); // 模拟工作
System.out.println("Thread " + id + " has finished");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}
}
}
AQS(AbstractQueuedSynchronizer)是Java并发包中的一个核心类,用于构建锁和其他同步组件。AQS通过一个FIFO队列来管理等待线程,并提供了独占模式和共享模式两种同步方式。
Semaphore
是基于AQS的共享模式实现的。AQS的state
变量表示当前可用的许可数量。Semaphore
通过调用AQS的acquireShared()
和releaseShared()
方法来实现许可的获取和释放。
以下是Semaphore
的部分源码分析:
public class Semaphore implements java.io.Serializable {
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {
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;
}
}
}
}
static final class NonfairSync extends Sync {
NonfairSync(int permits) {
super(permits);
}
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
}
static final class FairSync extends Sync {
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;
}
}
}
}
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
public void release() {
sync.releaseShared(1);
}
}
Semaphore
支持公平性和非公平性两种模式。在公平模式下,等待时间最长的线程优先获取许可;在非公平模式下,线程获取许可的顺序不确定。
Semaphore fairSemaphore = new Semaphore(10, true); // 公平信号量
Semaphore nonFairSemaphore = new Semaphore(10, false); // 非公平信号量
acquire()
方法会阻塞线程直到获取许可,但线程在等待过程中可能会被中断。acquireUninterruptibly()
方法则不会响应中断。
semaphore.acquireUninterruptibly(); // 不可中断的获取许可
tryAcquire(long timeout, TimeUnit unit)
方法允许线程在指定的时间内尝试获取许可,如果超时则返回false
。
boolean acquired = semaphore.tryAcquire(1, TimeUnit.SECONDS); // 尝试在1秒内获取许可
tryAcquire()
方法尝试获取许可,如果许可可用则立即返回true
,否则返回false
。
boolean acquired = semaphore.tryAcquire(); // 尝试获取许可
在使用Semaphore
时,如果多个线程相互等待对方释放许可,可能会导致死锁。为了避免死锁,应确保线程获取和释放许可的顺序一致。
如果线程获取许可后没有释放许可,可能会导致资源泄漏。因此,务必在finally
块中释放许可。
try {
semaphore.acquire();
// 访问共享资源
} finally {
semaphore.release();
}
在高并发场景下,Semaphore
的性能可能会成为瓶颈。可以通过调整许可数量、使用非公平模式或优化代码逻辑来提高性能。
CountDownLatch
用于等待一组线程完成某个任务,而Semaphore
用于控制对共享资源的访问。CountDownLatch
是一次性的,而Semaphore
可以重复使用。
CyclicBarrier
用于等待一组线程到达某个屏障点,然后同时继续执行。Semaphore
则用于控制对共享资源的访问。
ReentrantLock
是一种独占锁,同一时间只能有一个线程持有锁。Semaphore
则允许多个线程同时访问共享资源。
Semaphore
是Java并发编程中一个非常重要的同步组件,基于AQS实现,用于控制对共享资源的访问。通过合理使用Semaphore
,可以有效地管理多线程环境下的资源竞争问题。本文详细介绍了Semaphore
的基本使用、底层实现、高级用法以及注意事项,并与其他同步组件进行了比较。希望本文能帮助读者更好地理解和应用Semaphore
。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。