大家好,我是Semaphore,我的中文名字是“Semaphore”,来自JUC家族(java.util.concurrent)。我们家族有很多优秀的成员,比如:CountDownLatch:等待其他线程执行完再执行某个线程,CyclicBarrier:阻塞一组线程循环直到到达某个事件,当然我不比他们弱.以下是我的简历。希望各位读者给个好评,三连好评。提前谢谢~基本信息名称:Semaphore中文名称:(count)semaphore诞生日期:JDK1.5诞生地:JUC(java.util.concurrent)用途:Java中的一个同步器。与CountDownLatch和CyclicBarrier不同,Semaphore用于管理许可证。当线程调用acquire()方法时,如果没有许可证,只有在有许可证的情况下,线程才会阻塞并等待直到Proceed。使用release()方法颁发许可证(颁发许可证)。当调用acquire()方法时,如果有证书,则减少license,继续执行代码。如果没有certificate,就只能阻塞等待license,而Semaphore是在创建时声明最大licenses数量的。专业技能我的专业技能是“管理证书”,使用这个技能可以轻松实现“限流”功能。什么是限流?比如,五一假期马上就要到了。届时,前往各个景点的人流量会很大,但每个景点所能容纳的人数有限。比如西安的大唐芙蓉园,它每天的承载量是6万人次,也就是说每天最多可以来6万人次,但是五一期间来的人会很多,因为比如突然来了10万人,那么这个时候,他们只能“限流”排队等候入园。也就是说,大唐乐园将允许6万人先进去,其余人在门口排队等候。当有人出来时,另一个排队的人将被允许进入。工作人员始终将人数控制在6万人以下。这样做的目的是为了让玩家有一个好的体验,以免引发一些意外,比如踩踏等,在一定程度上保证社会稳定,也便于建立良好的口碑景区和以后的正常运营,这种排队限制最大人数的行为就是“限流”。再举个例子,比如车辆的数量限制,这也是限流的常见场景。这样做的好处是,一方面可以保护环境,尽可能减少碳排放,另一方面可以有效缓解上下班高峰期的拥堵。尤其是在西安,很难想象如果不限号会发生怎样的拥堵?(PS:让原本不富裕的生活变得更糟了。。。)让我们从生活中的例子回到程序,假设一个程序只能为10万人提供服务,突然有一天因为一个热点事件,短时间内系统的访问量增加到50万,那么直接的结果就是系统崩溃,没有人可以使用系统,显然只有一小部分人可以使用它远不止于此符合我们的预期比没有人能用,所以这时候就得用“限流”了。使用Semaphore实现限流Semaphore在创建的时候可以设置证书的个数,相当于设置限流的最大值,然后通过release()方法发布证书,通过acquire阻塞等待证书()方法,使其通过控制证书的方式来实现限流功能。项目体验下面我们通过代码来演示Semaphore的使用。我们以停车场限流为例。假设整个停车场只有2个车位(车位很少,但足以说明问题),但是有5辆车过来停车。显然,停车位不够用。这个时候我们需要保证停车场最多只能有2辆车。接下来我们使用Semaphore来实现车辆的限流功能。具体实现代码如下:importjava.util.Date;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;importjava.util.concurrent.Semaphore;/***作者:雷哥*作者:Java中文社区*/publicclassSemaphoreExample{//创建一个信号量staticSemaphoreSemaphore=newSemaphore(2);publicstaticvoidmain(String[]args){//创建5个固定线程ExecutorServicethreadPool=Executors.newFixedThreadPool(5);//定义执行任务Runnablerunnable=newRunnable(){@Overridepublicvoidrun(){//获取当前线程的名称Stringtname=Thread.currentThread().getName();System.out.println(String.format("老司机:%s,在停车场外排队,时间:%s",tname,newDate()));try{//执行这一行,让所有线程先排队等待进入停车场Thread.sleep(100);//执行阻塞semaphore.acquire();System.out.println(String.format("老司机:%s,已进入停车场,时间:%s",tname,newDate()));Thread.sleep(1000);System.out.println(String.format("老司机:%s,离开停车场lot,time:%s",tname,newDate()));//释放锁semaphore.release();}赶上(我interruptedExceptione){e.printStackTrace();}}};//执行任务1threadPool.submit(runnable);//执行任务2threadPool.submit(runnable);//执行任务3threadPool.submit(runnable);//执行任务task4threadPool.submit(runnable);//执行任务5threadPool.submit(runnable);//线程池任务执行完后关闭threadPool.shutdown();}}以上代码执行结果如下:来自以上结果,我们可以看到当有5辆车同时需要进入停车场时,由于停车场只有2个车位,所以停车场最多只能容纳2辆车。这时我们使用acquire方法(阻塞等待)和release方法(颁发证书)成功实现了限流功能,使停车场的车辆数量始终控制在2辆以下(等于或小于2辆车)。我个人(Semaphore)实现证书控制有两种方式,公平模式和非公平模式。当然,为了执行性能,我默认采用了不公平的方式。具体实现可以看源码:publicSemaphore(intpermits){sync=newNonfairSync(permits);//非公平模式}关于公平模式和非公平模式acquire()的调用顺序,公平模式遵循先进先出(FIFO)原则;非公平模式是抢占式的,即有可能刚放出一个license就有新的获取线程去获取license,前面有等待的线程。显然,使用非公平模式的性能更高,因为它会把license发给刚准备好的线程,而不是按顺序“叫号”。使用公平模式当然也可以手动选择以公平模式运行Semaphore。Semaphore提供了两个构造函数。源码如下:publicSemaphore(intpermits){sync=newNonfairSync(permits);}publicSemaphore(intpermits,booleanfair){sync=fair?newFairSync(permits):newNonfairSync(permits);}如果要使用公平模式,可以使用第二个构造函数Semaphore(intpermits,booleanfair),将fair值设置为true,以公平方式获取证书。其他补充我还提供了一些其他的方法来实现更多的功能,具体如下:intavailablePermits():返回该信号量当前可用的许可数。intgetQueueLength():返回等待获取许可的线程数。booleanhasQueuedThreads():是否有线程等待获取许可。booleanisFair():查询信号量是使用公平模式还是非公平模式,如果信号量使用公平模式则返回true。voidrelease(intpermits):释放给定数量的许可,将它们返回给信号量。tryAcquire():从这个信号量中获取一个许可,只能在调用时使用。tryAcquire(intpermits):从该信号量获取给定数量的许可,前提是它们在调用时都可用。tryAcquire(intpermits,longtimeout,TimeUnitunit):如果在给定的等待时间内所有许可都可用并且当前线程未被中断,则从该信号量获取给定数量的许可。tryAcquire(longtimeout,TimeUnitunit):如果在给定的等待时间内可用并且当前线程尚未达到中断状态,则从该信号量获取权限。voidreducePermits(intreduction):通过reduction减少可用许可的数量,它是一种受保护的方法。CollectiongetQueuedThreads():返回所有等待获取许可的线程的集合,这是一个受保护的方法。总结Semaphore信号量是用来管理一组证书的。默认情况下,它使用不公平的方法来管理证书。这样做的目的是为了达到高性能。Semaphore包含两个重要的方法:release()方法颁发许可证证书;acquire()方法会阻塞并等待证书。当线程调用acquire()方法时,只有有证书才能继续执行,所以可以使用Semaphore来实现限流。
