java高并发程序设计总结四:JDK并发包之信号量Semaphore

时间:2021-12-14 23:50:50

概念

信号量是对锁的一种扩展。不管是对于synchronized还是对于ReentrantLock锁来说,一次都只允许一个线程获取锁资源,其余的线程都只能处于等待状态。而信号量可以指定多个线程同时访问某一个资源,包括锁资源
Semaphore主要提供了一下两个构造函数
public Semaphore(int permits);初始化信号量并指定能同时访问的线程数量

public Semaphore(int permits, boolean fair);在实现上面的要求时额外指定是否使用公平锁
“公平信号量”和”非公平信号量”的释放信号量的机制是一样的!不同的是它们获取信号量的机制:线程在尝试获取信号量许可时,对于公平信号量而言,如果当前线程不在CLH队列的头部,则排队等候;而对于非公平信号量而言,无论当前线程是不是在CLH队列的头部,它都会直接获取信号量。(CLH是其内部维护的一个等待队列)


主要方法

构造信号量对象时,必须指定信号量的准入数,即同时能申请多少个许可。(synchronized和ReentrantLock就只能申请一个许可)信号量的主要逻辑方法有:
public void acquire() throws InterruptedException
尝试获得一个准入的许可,如果无法获得,线程就会一直等待,直接获得了一个许可
或者当前线程被中断,后者会抛出中断异常

public void acquireUnInterruptibly();
与acquire作用相同,区别在于不响应中断

public boolean tryAcquire();
尝试去申请一个许可,如果能获得,则返回true,并获得许可,否则立马返回false,不会等待

public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException;
尝试在指定时间内获取一个许可,如果能获得,则返回true,并获得许可,否则等待timeout时间后
还是没有获得,就返回false;在timeout时间内如果被中断,则会抛出中断异常

public void release();
用于在线程访问资源结束之后释放一个许可


信号量的使用代码

public class Main{
    public static void main(String[] args){
        //信号量测试
        //创建能容纳20个线程的线程池
        ExecutorService exec = Executors.newFixedThreadPool(20);
        semthread sem = new semthread();
        for(int i=0; i<20; i++)
            exec.submit(sem);
    }

    //使用信号量设置五个线程同时进入,并设置成公平锁
    public static Semaphore sema = new Semaphore(5,true);

    public static class semthread extends Thread{

        public void run(){
            try{
                sema.acquire();
                Thread.sleep(2000);
                System.out.println("hello "+Thread.currentThread().getId());
                sema.release();
            }
            catch(Exception e){
                e.printStackTrace();
                System.out.println("semaphore exception...");
            }
        }
    }
}

//代码运行结果:将会在两秒的时间间隔内以五个一组的形式输出

而且在20的线程执行完毕之后,发现程序还没有结束,这是因为我们在这里使用的是线程池,
线程池中的线程在执行完毕之后不会被销毁,而是重回线程池,等待被使用,期间一直处于等待状态,
所以程序不会被结束


参考文献

java高并发程序设计第三章–Semaphore信号量
Semaphore信号量的底层实现和原理