信号量(Semaphores)


前言

记得当年在学习《操作系统》时,对信号量和P,V操作这一块一直搞不太清楚,书中的伪代码着实令人费解(看得懂,就是下笔就忘)。上学时未解决的问题,现在终究还是要解决呀。今天就聊聊java.util.concurrent.Semaphore。

概念

信号量(Semaphores)机制是一种卓有成效的进程同步工具,由荷兰学者Dijkstra提出的(这哥们貌似还提出过图的最短路径算法)。

信号量的值仅能由PV操作来改变。
p操作(wait):申请一个单位资源,进程进入。
v操作(signal):释放一个单位资源,进程出来。
一般来说,信号量S≥0时,S表示可用资源的数量。执行一次P操作意味着请求分配一个单位资源,因此S的值减1;当S<0时,表示已经没有可用资源,请求者必须等待别的进程释放该类资源,它才能运行下去。而执行一个V操作意味着释放一个单位资源,因此S的值加1;若S≤0,表示有某些进程正在等待该资源,因此要唤醒一个等待状态的进程,使之运行下去。
通俗来说就是信号量会有一个初始值S(S≥0),这时有线程申请资源,S就会减1(S--);当S==0时,再来申请资源的线程就会阻塞;已申请到资源的进程执行完毕后,会释放资源,S会加1(S++);此时S>0,阻塞的线程就会获取到资源,获取到资源的同时,S会进行减1操作。也就是说,最多只能有S个线程同时占用资源。

Java中的Semaphore

先上一张Semaphore的方法图


Semaphore

由图可以看出在构造方法里会传入一个int类型的参数,

/**
 * Creates a {@code Semaphore} with the given number of
 * permits and nonfair fairness setting.
 *
 * @param permits the initial number of permits available.
 *        This value may be negative, in which case releases
 *        must occur before any acquires will be granted.
 */
public Semaphore(int permits) {
    sync = new NonfairSync(permits);
}

由注释可以看出该permits可以为负数,但是在这种情况下释放资源(releases)必须在申请资源(acquires)之前调用。

/**
 * Acquires a permit from this semaphore, blocking until one is
 * available, or the thread is {@linkplain Thread#interrupt interrupted}.
 * 从信号量中获得一个许可,线程阻塞直到获取到许可,或者线程被关闭
 * 

Acquires a permit, if one is available and returns immediately, * reducing the number of available permits by one. * 如果有一个许可是可用的,就获取到该许可,并使可用许可数目减少1 *

If no permit is available then the current thread becomes * disabled for thread scheduling purposes and lies dormant until * one of two things happens: *

    *
  • Some other thread invokes the {@link #release} method for this * semaphore and the current thread is next to be assigned a permit; or *
  • Some other thread {@linkplain Thread#interrupt interrupts} * the current thread. *
* *

If the current thread: *

    *
  • has its interrupted status set on entry to this method; or *
  • is {@linkplain Thread#interrupt interrupted} while waiting * for a permit, *
* then {@link InterruptedException} is thrown and the current thread's * interrupted status is cleared. * * @throws InterruptedException if the current thread is interrupted */ public void acquire() throws InterruptedException { sync.acquireSharedInterruptibly(1); } /** * Releases a permit, returning it to the semaphore. * 释放一个许可,把它交给信号量 *

Releases a permit, increasing the number of available permits by * one. If any threads are trying to acquire a permit, then one is * selected and given the permit that was just released. That thread * is (re)enabled for thread scheduling purposes. * *

There is no requirement that a thread that releases a permit must * have acquired that permit by calling {@link #acquire}. * Correct usage of a semaphore is established by programming convention * in the application. */ public void release() { sync.releaseShared(1); }

那显而易见,releases()方法和acquires()方法就分别执行释放资源和申请资源的操作了。

代码

下面来用代码来说明信号量(Semaphore)在Java中的使用

public static void main(String[] args) throws InterruptedException {
    Semaphore semaphore = new Semaphore(3);
    ExecutorService executorService=Executors.newCachedThreadPool();
    for (int i=0;i<20;i++){
        executorService.execute(()->{
            try {
                semaphore.acquire();
                System.out.println(Thread.currentThread().getName()+" 进入!");
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                semaphore.release();
                System.out.println(Thread.currentThread().getName()+" 释放!");
            }
        });
    }
    executorService.shutdown();
}

20个线程都在请求信号量,但信号量只允许有3个线程进入,所以只有获得信号量的线程结束释放信号量后才能有新的线程进入。如果我们不释放信号呢,就只能打印出3句log了。

pool-1-thread-1 进入!
pool-1-thread-5 进入!
pool-1-thread-2 进入!

控制台打印的结果和我们设想的一致。

应用

Semaphore可以用于流量控制,特别是对并发数有限制的场景。如数据库同时只允许有20个线程访问,就可以使用信号量来实现。

你可能感兴趣的:(信号量(Semaphores))