Java中的Semaphore信号量深入解析
作者:♛薄情痞子♛
Semaphore信号量
Semaphore是Java里面另外一个基本的并发工具包类,主要的的作用是用来保护共享资源的访问的,也就是仅仅允许一定数量的线程访问共享资源。
Semaphore维护了有限数量的许可证,只有得到了许可证的线程才能进行共享资源的访问,如果得不到许可证,说明当前共享资源的访问已经达到最大限制,所以会挂起当前线程,直到前面的线程处理完任务之后,把许可证归还,后面排队的线程才有机会获取,然后处理任务。
这里面有两个注意点:
(1)大多数时候使用Semaphore都应该是公平模式,默认是非公平模式,如果需要公平模式可以在构造函数里面指定,公平性可以 保证先进先出,不会有线程饥饿问题出现,非公平模式,不保证顺序,吞吐量会更好一些。
(2)共享资源的访问,一般指的是读取,而不是更新,这里面不要做对共享变量的修改,除非你使用同步块来保证。
下面我们来看下Semaphore的构造方法:
Semaphore(int permits) //非公平模式指定最大允许访问许可证数量 Semaphore(int permits, boolean fair)//可以通过第二个参数控制是否使用公平模
一些常用的方法:
acquire() //申请获取一个许可证,如果没有许可证,就阻塞直到能够获取或者被打断 availablePermits() // 返回当前有多少个有用的许可证数量hasQueuedThreads()//查询是否有线程正在等待获取许可证 drainPermits()//获得并返回所有立即可用的许可证数量 getQueuedThreads()//返回一个List包含当前可能正在阻塞队列里面所有线程对象 getQueueLength()//返回当前可能在阻塞获取许可证线程的数量 hasQueuedThreads()//查询是否有线程正在等待获取许可证 isFair()//返回是否为公平模式 reducePermits(int reduction)//减少指定数量的许可证 reducePermits(int reduction)//释放一个许可证 release(int permits)//释放指定数量的许可证 tryAcquire()//非阻塞的获取一个许可证
无论是Semaphore还是CountDonwLatch或者是CyclicBarrier,其实我们都可以通过Lock接口+Condition条件队列功能来模拟实现,但是不够抽象所以才出现了AQS这个抽象的面向开发者同步框架,比如这个Semaphore,我们看下如何使用Lock实现:
public class SemaphoreDemo2 { private final Lock lock=new ReentrantLock(true); private final Condition condition=lock.newCondition(); private int permit; public SemaphoreDemo2(int permit) { this.permit=permit; } private void acquire(){ lock.lock(); try{ if(permit==0){ condition.await();//如果超过限制,就进入条件阻塞队列 } System.out.println(Thread.currentThread().getName()+" 获得资源 .... "); permit--; } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } private void release(){ lock.lock(); try{ permit++; condition.signalAll(); //每当有一个释放令牌,就唤醒所有等待的线程 }finally { lock.unlock(); } } }
下面我们看一下简单的使用例子:
Semaphore semaphore=new Semaphore(3); Runnable runnable=new Runnable() { @Override public void run() { try { semaphore.acquire(); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " 访问资源......"); semaphore.release(); } }; for (int i = 0; i < 5; i++) { Thread thread=new Thread(runnable); thread.start(); } Thread.sleep(3000);
输出结果:
Thread-0 访问资源......
Thread-1 访问资源......
Thread-2 访问资源......
Thread-4 访问资源......
Thread-3 访问资源......
注意上面的例子只有3个许可证,我们运行了5个线程,所以同时最多只能运行3个线程,另外两个会阻塞直到前面的线程归还了许可证。
Semaphore底层原理:
Semaphore底层与CountDownLatch类似都是通过AQS的共享锁机制来实现的,指定的数量会设置到AQS里面的state里面,然后对于每一个 调用acquire方法线程,state都会减去一,如果state等于0,那么调用该方法的线程会被添加到同步队列里面,同时使用 LockSupport.park方法挂起等待,知道有线程调用了release方法,会对state加1,然后唤醒共享队列里面的线程,注意这里如果是 公平模式,就直接唤醒下一个等待线程即可,如果是非公平模式就允许新加入的线程与已有的线程进行竞争,谁先得到就是谁的,如果新加入的 竞争失败,就会走公平模式进入队列排队。
总结:
本文主要介绍了并发工具包Semaphore其主要作用来限制对于共享资源的访问,接着我们又介绍了其特点,使用及注意事项,然后又给出了使用其他同步工具Lock+Condition实现的Semaphore
到此这篇关于Java中的Semaphore信号量深入解析的文章就介绍到这了,更多相关Semaphore信号量深入解析内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!