概念
信号量是对锁的一种扩展。不管是对于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){
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信号量