信号量
信号量
信号量是什么
信号量是一种用于控制对共享资源的访问的同步机制,他可以限制同时访问共享资源的线程数量,从而避免资源的竞争和冲突。
使用场景:用于多线程的并发控制,类似于限流。
信号量和限流的异同
信号量和限流的使用场景侧重点不同:
- 信号量通常用于控制对共享资源的访问;
- 限流用于控制系统的输入和输出流量,防止系统过载;
关注的维度不同
- 限流通常是接口维度或系统维度的,一般是分布式下控制的;
- 信号量是单机节点下控制的线程并发。
相似之处,都涉及对资源或流量的控制,以保证系统的稳定性;
信号量怎么用
信号量可以使用 java.util.concurrent.Semaphore 类来实现。
你可以通过创建一个信号量对象,然后使用 acquire() 方法来获取信号量,使用 release() 方法来释放信号量。
import java.util.concurrent.Semaphore;
public class SemaphoreExample {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(1); // 创建一个初始值为1的信号量
try {
semaphore.acquire(); // 获取信号量
// 在这里执行需要控制访问的代码
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release(); // 释放信号量
}
}
}
用锁模拟信号量
自己实现模拟实现一个信号量,便于理解内部原理
import java.util.concurrent.locks.ReentrantLock;
public class CustomSemaphore {
private final ReentrantLock lock = new ReentrantLock();
private volatile int permits;
public CustomSemaphore(int initialPermits) {
this.permits = initialPermits;
}
public void acquire() throws InterruptedException {
lock.lock();
while (permits == 0) {
lock.unlock();
Thread.sleep(100); // 休眠一段时间后再尝试获取锁
lock.lock();
}
permits--;
lock.unlock();
}
public void release() {
lock.lock();
permits++;
lock.unlock();
}
}
信号量满的情况下,是线程等待吗?
是的,当信号量满的情况下,线程会进入等待状态,直到有其他线程释放许可为止。
信号量实现的原理是什么?
信号量的原理是使用一个计数器来控制对共享资源的访问,
当线程希望访问共享资源时,需要先获取信号量。
如果信号量的计数器大于0,则线程可以获取该信号量并将计数器减一,表示占用了一个资源;
如果计数器为0,则线程需要等待,直到有其他线程释放资源,使得计数器增加。
当线程访问完共享资源后,需要释放信号量,将计数器加一,表示释放了一个资源,其他等待的线程可以获取信号量继续访问共享资源。
Semaphore 的内部实现
Semaphore 的内部实现基于同步器 AbstractQueuedSynchronizer (AQS)。
AQS 提供了一个框架,可以用来构建不同类型的同步器,Semaphore 就是其中一种。
Semaphore 内部通过 AQS 的 acquire 和 release 方法来实现许可的获取和释放,以及等待队列的管理。
Semaphore 在内部维护一个计数器,用来表示可用的许可数量,通过 AQS 的状态来实现对计数器的操作和线程的阻塞唤醒。
Semaphore是怎么实现队列等待的
Semaphore 内部通过使用 AQS(AbstractQueuedSynchronizer)来实现队列等待。
当线程尝试获取许可时,如果许可数量不足,线程会被加入到 AQS 的等待队列中,然后被阻塞挂起。
当其他线程释放许可时,AQS 会按照特定的策略唤醒等待队列中的线程,使其有机会再次尝试获取许可。
这样就实现了基于队列的等待机制。
AQS(AbstractQueuedSynchronizer)是 Java 中用于构建同步器的框架,
它提供了一种基于 FIFO 等待队列的同步器实现方式,可以用来构建各种类型的同步器,如 ReentrantLock、Semaphore 等。
AQS 的核心思想是通过内置的队列和状态来实现线程的阻塞和唤醒,从而实现对共享资源的访问控制。