信号量

发布时间 2023-12-17 14:30:33作者: 路迢迢

信号量

信号量是什么

信号量是一种用于控制对共享资源的访问的同步机制,他可以限制同时访问共享资源的线程数量,从而避免资源的竞争和冲突。
使用场景:用于多线程的并发控制,类似于限流。

信号量和限流的异同

信号量和限流的使用场景侧重点不同:

  1. 信号量通常用于控制对共享资源的访问;
  2. 限流用于控制系统的输入和输出流量,防止系统过载;

关注的维度不同

  1. 限流通常是接口维度或系统维度的,一般是分布式下控制的;
  2. 信号量是单机节点下控制的线程并发。

相似之处,都涉及对资源或流量的控制,以保证系统的稳定性;

信号量怎么用

信号量可以使用 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 的核心思想是通过内置的队列和状态来实现线程的阻塞和唤醒,从而实现对共享资源的访问控制。