一、信号量(Semaphore)
信号量为多线程协作提供了更为强大的控制方法。广义上说,信号量是对锁的扩展。无论是内部锁synchronized还是重入锁ReentrantLock,一次都只允许一个线程访问一个资源,而信号量却可以指定多个线程,同时访问某一个资源。信号量主要提供了以下构造函数:
public Semaphore(int permits) public Semaphore(int permits, boolean fair) //第二个参数可以指定是否公平
在构造信号量对象时,必须要指定信号量的准入数,即同时能申请多少个许可。当每个线程每次只申请一个许可时,这就相当于指定了同时有多少个线程可以访问某一个资源。信号量的主要逻辑方法有
public void acquire() public void acquireUninterruptibly() public boolean tryAcquire() public boolean tryAcquire(long timeout, TimeUnit unit) public void release()
acquire()方法尝试获得一个准入的许可。若无法获得,则线程会等待,直到有线程释放一个许可或者当前线程被中断。acquireUninterruptibly()方法和acquire()方法类似,但是不响应中断。tryAcquire()尝试获得一个许可,如果成功返回true,失败则返回false,它不会进行等待,立即返回。release()用于在线程访问资源结束后,释放一个许可,以使其他等待许可的线程可以进行资源访问。
public class SemaphoreTest implements Runnable { private Semaphore semaphore = new Semaphore(5); @Override public void run() { try { semaphore.acquire(); Thread.sleep(2000); System.out.println(Thread.currentThread().getId() + "has done"); } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); } } public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(20); SemaphoreTest semaphoreTest = new SemaphoreTest(); for(int i=0; i < 20; i++) { executor.submit(semaphoreTest); } } }
程序打印
14has done
13has done
12has done
11has done
10has done
15has done
16has done
17has done
18has done
19has done
20has done
22has done
24has done
23has done
21has done
27has done
26has done
25has done
29has done
28has done
使用场景:用于那些资源有明确访问数量限制的场景,常用于限流。数据库连接池,同时进行连接的线程有数量限制,连接不能超过一定的数量,当连接达到了限制数量后,后面的线程只能排队等前面的线程释放了数据库连接才能获得数据库连接。