简介

Semaphore是一种在多线程环境下使用的设施,该设施负责协调各个线程,以保证它们能够正确、合理的使用公共资源的设施,也是操作系统中用于控制进程同步互斥的量。

Semaphore是一种计数信号量,用于管理一组资源,内部是基于AQS的共享模式。它相当于给线程规定一个量从而控制允许活动的线程数

Semaphore用于限制可以访问某些资源(物理或逻辑的)的线程数目,他维护了一个许可证集合,有多少资源需要限制就维护多少许可证集合,假如这里有N个资源,那就对应于N个许可证,同一时刻也只能有N个线程访问。

举个例子:
相信在学生时代都去餐厅打过饭,假如有3个窗口可以打饭,同一时刻也只能有3名同学打饭。第四个人来了之后就必须在外面等着,只要有打饭的同学好了,就可以去相应的窗口了。

一个线程获取许可证就调用acquire方法,用完了释放资源就调用release方法。

自定义Lock

public class SemaphoreTest {

    public static void main(String[] args) {
        final SemaphoreLock lock = new SemaphoreLock();
        for (int i = 0; i < 2; i++) {
            new Thread(()->{
                try {
                    lock.lock();
                    System.out.println(Thread.currentThread().getName() + " get the lock.");
                    Thread.sleep(3000);
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    System.out.println(Thread.currentThread().getName() + " release the lock.");
                    lock.unlock();
                }
            }).start();
        }

    }



    static class SemaphoreLock{
        private final Semaphore semaphore = new Semaphore(1);

        public void lock() throws InterruptedException {
            semaphore.acquire();
        }

        public void unlock()  {
            semaphore.release();
        }
    }
}

结果

Thread-0 get the lock.
Thread-0 release the lock.
Thread-1 get the lock.
Thread-1 release the lock.

跟synchronized的区别

  • 可以看出最大的区别就是可以控制多个线程访问资源,而不是只用一个线程

构造方法

public Semaphore(int permits)
public Semaphore(int permits , boolean fair)
  • permits:同一时间可以访问资源的线程数目
  • fair:尽可能的保证公平

重要方法

public void acquire() throws InterruptedException
public void release()
  • acquire()获取一个许可证,如果许可证用完了,则陷入阻塞。可以被打断。
  • release()释放一个许可证
acquire(int permits) 
public void release(int permits)

acquire多个时,如果没有足够的许可证可用,那么当前线程将被禁用以进行线程调度,并且处于休眠状态,直到发生两件事情之一:

  • 一些其他线程调用此信号量的一个release方法,当前线程旁边将分配许可证,并且可用许可证的数量满足此请求;
  • 要么一些其他线程interrupts当前线程。

release多个时,会使许可证增多,最终可能超过初始值

public boolean tryAcquire(int permits)
public boolean tryAcquire(int permits,
                          long timeout,
                          TimeUnit unit)
                   throws InterruptedException
  • 尝试去拿,拿到返回true

其他方法

public int availablePermits()
  • 返回此信号量中当前可用的许可数。
protected Collection<Thread> getQueuedThreads()
public final int getQueueLength()
  • getQueuedThreads返回正在阻塞的线程集合
  • getQueueLength返回阻塞获取的线程数
public void acquireUninterruptibly()
public void acquireUninterruptibly(int permits)
  • 可以不被打断获取许可证
public int drainPermits()
  • 获取当前全部的许可证