Semaphore是一个计数信号量,它的本质是一个"共享锁"。
信号量维护了一个信号量许可集。线程可以通过调用acquire()来获取信号量的许可;当信号量中有可用的许可时,线程能获取该许可;否则线程必须等待,直到有可用的许可为止。 线程可以通过release()来释放它所持有的信号量许可。
Semaphore为并发包中提供用于控制某资源同时可以被几个线程访问的类
Semaphore是一种基于计数的信号量。它可以设定一个阈值,基于此,多个线程竞争获取许可信号,做完自己的申请后归还,超过阈值后,线程申请许可信号将会被阻塞。Semaphore可以用来构建一些对象池,资源池之类的,比如数据库连接池,我们也可以创建计数为1的Semaphore,将其作为一种类似互斥锁的机制,这也叫二元信号量,表示两种互斥状态。它的用法如下:
- // 创建一个计数阈值为5的信号量对象
- // 只能5个线程同时访问
- Semaphore semp = new Semaphore(5);
- try {
- // 申请许可
- semp.acquire();
- try {
- // 业务逻辑
- } catch (Exception e) {
- } finally {
- // 释放许可
- semp.release();
- }
- } catch (InterruptedException e) {
- }
例: 20个人去银行存款,但是该银行只有两个办公柜台,有空位则上去存钱,没有空位则只能去排队等待
package com.xhj.thread; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; /** * 线程信号量Semaphore的运用 * * @author XIEHEJUN * */ public class SemaphoreThread { private int a = 0; /** * 银行存钱类 */ class Bank { private int account = 100; public int getAccount() { return account; } public void save(int money) { account += money; } } /** * 线程执行类,每次存10块钱 */ class NewThread implements Runnable { private Bank bank; private Semaphore semaphore; public NewThread(Bank bank, Semaphore semaphore) { this.bank = bank; this.semaphore = semaphore; } @Override public void run() { int b = a++; if (semaphore.availablePermits() > 0) { System.out.println("线程" + b + "启动,进入银行,有位置立即去存钱"); } else { System.out.println("线程" + b + "启动,进入银行,无位置,去排队等待等待"); } try { semaphore.acquire(); bank.save(10); System.out.println(b + "账户余额为:" + bank.getAccount()); Thread.sleep(1000); System.out.println("线程" + b + "存钱完毕,离开银行"); semaphore.release(); } catch (InterruptedException e) { e.printStackTrace(); } } } /** * 建立线程,调用内部类,开始存钱 */ public void useThread() { Bank bank = new Bank(); // 定义10个新号量 Semaphore semaphore = new Semaphore(2); // 建立一个缓存线程池 ExecutorService es = Executors.newCachedThreadPool(); // 建立20个线程 for (int i = 0; i < 10; i++) { // 执行一个线程 es.submit(new Thread(new NewThread(bank, semaphore))); } // 关闭线程池 es.shutdown(); // 从信号量中获取两个许可,并且在获得许可之前,一直将线程阻塞 semaphore.acquireUninterruptibly(2); System.out.println("到点了,工作人员要吃饭了"); // 释放两个许可,并将其返回给信号量 semaphore.release(2); } public static void main(String[] args) { SemaphoreThread test = new SemaphoreThread(); test.useThread(); } }
面试题思考:
在很多情况下,可能有多个线程需要访问数目很少的资源。假想在服务器上运行着若干个回答客户端请求的线程。这些线程需要连接到同一数据库,但任一时刻
只能获得一定数目的数据库连接。你要怎样才能够有效地将这些固定数目的数据库连接分配给大量的线程?
答:1.给方法加同步锁,保证同一时刻只能有一个人去调用此方法,其他所有线程排队等待,但是此种情况下即使你的数据库链接有10个,也始终只有一个处于使用状态。这样将会大大的浪费系统资源,而且系统的运行效率非常的低下。
2.另外一种方法当然是使用信号量,通过信号量许可与数据库可用连接数相同的数目,将大大的提高效率和性能。