Java多线程/并发22、信号量Semaphore

Semaphore通常用于设置一些资源(如数据库连接池,程序某个变量)可供线程使用的数量,它有两个很常用的方法是acquire()和release()。

打个通俗的比方:一个饭店有10间包房,客人消费前先到前台领一张包房券(调用一次acquire 包房券-1),进入包房用餐直到酒足饭饱后,再到前台退回包房券(包房券+1)并结帐(调用一次release())。而前台工作人员呢,手上一共10张包房券(对应10间房),这时来了客人要吃饭,工作人员看看手上是否还有券,有就发给要消费的客人;没有,说明包房被订满了,就让客户站在外面排队等侯空位。

Semaphore有公平性非公平性两种实现模式。现在外面有很多客户在排队等位,突然有一队客户跑来对工作人员说:我饿得不行了,先让我进去吧。该工作人员有个属性(boolean fair),老板设置为铁面无私时,他就会否决客人的要求,因为先排队的先进。如果老板设置为圆滑世故时,他就会想想,你都饿成这样了,再不让你进去等会晕了可负不起责,赶紧进去吧。

构造方法:

  • 创建信号量并指定许可证的数量
Semaphore(int permits) 
  • 创建信号量指定许可证的数量,并设置线程获取许可证的公正性
 Semaphore(int permits, boolean fair) 

主要方法:

  • void acquire() 从信号量获取一个许可,如果无可用许可前,将一直阻塞等待。
  • void acquire(int permits) 获取指定数目的许可,如果无可用许可前,也将会一直阻塞等待。
  • boolean tryAcquire() 从信号量尝试获取一个许可,如果无可用许可,直接返回false,不会阻塞。
  • boolean tryAcquire(int permits) 尝试获取指定数目的许可,如果无可用许可直接返回false,不会阻塞。
  • boolean tryAcquire(int permits,long timeout, TimeUnit unit) 在指定的时间内尝试从信号量中获取许可,如果在指定的时间内获取成功,返回true,否则返回false。阻塞。
  • void release() 释放一个许可,别忘了在finally中使用。注意:多次调用该方法,会使信号量的许可数增加,达到动态扩展的效果,如:初始permits为1, 调用了两次release,最大许可会改变为2
  • int availablePermits() 获取当前信号量可用的许可数量。

简单使用例子:

public class SemaphoreDemo {
    public static void main(String[] args) {
        final Semaphore permit=new Semaphore(3,true);
        for(int i=0;i<4;i++){
            new Thread(new Runnable() {
                public void run() {
                    try {
                        /*阻塞线程用以获取许可证*/
                        permit.acquire();
                        System.out.println(Thread.currentThread().getName()
                                    + "获取许可成功。可用许可剩余:" + permit.availablePermits());
                        Thread.sleep(new Random().nextInt(100));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }finally{
                        System.out.println(Thread.currentThread().getName()
                                + "释放许可成功。可用许可剩余:" + (permit.availablePermits()+1));
                        /*阻塞线程用以释放许可证*/
                        permit.release();

                    }

                }
            }).start();
        }
    }
}

这里要注意:acquire()和release()之间代码,并非线程同步的。假设Semaphore的许可数设为5,那么就意味着有5个线程同时执行acquire()和release()之间代码。如果要线程同步执行,就需要自己去处理了。比如中间的代码要访问和改变一个公共变量,为保持事务性,就要加上synchronized{}块或者lock锁,使线程同步。

你可能感兴趣的:(Java多线程/并发)