Semaphore:信号量,就是资源数.
该类的功能:当很多线程在执行任务时,需要规定最大线程执行数,超过的部分没有获取到执行权利时,需要等待获取到权利的线程释放资源后,在获取执行任务的权利.
一,构造器:传入参数设置最大可执行任务线程数,通过构造器可以看出该类可生成公平获取资源数和非公平获取资源数,默认非公平
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits); }
//默认是非公平获取执行权利 public Semaphore(int permits) { sync = new NonfairSync(permits); }
二,实现方法主要是通过acquire()方法和release()方法来实现的,当然该类也实现了不同功能的acquire方法,从该类中可以看出.,下面我先具体分析下acquire()方法源码,其他的方法也就好理解多了
//该方法是可以接受中断信号的,当线程执行该方法且处在等待状态时,是可以被中断的,抛出异常 public void acquire(int permits) throws InterruptedException { if (permits < 0) throw new IllegalArgumentException();
//具体实现是根据公平获取资源还是非公平来分析 sync.acquireSharedInterruptibly(permits); }
public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); //该方法是判断是否有资源可获取,如果有则放回值是大于等于0的,无则回小于0,小于0则会执行下面方法 if (tryAcquireShared(arg) < 0) //该方法是把线程放入等待的线程队列,等待资源释放后,竞争执行资源
doAcquireSharedInterruptibly(arg); }
tryAcquireShared()
//该方法是非公平方法实现的方式 protected int tryAcquireShared(int acquires) { //自旋锁的形式因为该方法是未加锁的,多个线程竞争可能数据会发生改变,所以当数据发生改变后,在重新循环操作一遍 for (;;) { //该方法是非公平多的一步,判断等待队列中是否有线程在等待,有就直接跳出,没有进行后面代码 if (hasQueuedPredecessors()) return -1; int available = getState(); int remaining = available - acquires; //该判断remain小于0则没有资源可竞争直接返回该值,或者不小于0时,CAS操作state值.说明有资源获得,CAS操作失败则说明有其他线程操作过该值,则继续执行该循环的代码. if (remaining < 0 || compareAndSetState(available, remaining)) return remaining; } }
doAcquireSharedInterruptibly():
private void doAcquireSharedInterruptibly(int arg) throws InterruptedException { //将该线程放入线程等待队列 final Node node = addWaiter(Node.SHARED); boolean failed = true; try { //通过自旋锁实现等待功能 for (;;) { //获取排队线程 final Node p = node.predecessor(); //如果是true轮到该线程执行了 if (p == head) { //查看现在是否有资源空出,大于等于0则说明有,那该线程跳出自旋锁,不在等待,清空等待队列该线程的数据 int r = tryAcquireShared(arg); if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC failed = false; return; } } //判断该线程在等待的过程中是否接受到中断信息,有的话则抛出异常. if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } }
release()方法:该方法是线程获取到资源后,执行完任务后,要执行的方法.
public final boolean releaseShared(int arg) { //释放该线程获取的资源 if (tryReleaseShared(arg)) { //对共享锁的队列进行操作 doReleaseShared(); return true; } return false; }
案例:引用他人分享的内容
import java.util.concurrent.Semaphore; class SemaphoreDemo { /** * Semaphore:信号量 * 当前在多线程环境下被扩放使用,操作系统的信号量是个很重要的概念,在进程控制方面都有应用。 * Java 并发库 的Semaphore 可以很轻松完成信号量控制, * Semaphore可以控制某个资源可被同时访问的个数, * 通过 acquire() 获取一个许可,如果没有就等待, * 通过 release() 释放一个许可。 * 比如在Windows下可以设置共享文件的最大客户端访问个数。 */ static void test(int threadNum) { // Semaphore semaphore = new Semaphore(threadNum, true);// 是否公平 final Semaphore semaphore = new Semaphore(3);//最多同时允许 3 个线程并发访问 for (int i = 0; i < threadNum; i++) { new Thread(new Runnable() { @Override public void run() { try { System.out.println("线程" + Thread.currentThread().getName() + " 想要处理,剩余凭证:" + semaphore.availablePermits()); semaphore.acquire(); Thread.sleep((long) (Math.random() * 2000)); System.out.println("线程" + Thread.currentThread().getName() + " 处理完毕,归还凭证!"); // 如果注释下面这行,表示不释放凭证,那么后续线程将永远阻塞在semaphore.acquire();上 semaphore.release();// 用完后要释放,以给其它线程使用 } catch (Exception e) {} } }).start(); } } } public class Test { public static void main(String[] args) { SemaphoreDemo.test(5); } }
acquireUninterruptibly():该方法与acquire()区别在于该方法在等待的过程中是不能被中断的.
tryAcquire():该方法返回Boolean值,获取成功在放回true,失败则false,业务可根据结果做其他操作,避免无谓的等待
public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException:加入了获取资源的时间限制,成功则true,失败则false,可在获取过程中被中断抛出异常
public void acquire(int permits) throws InterruptedException:该方法是获取多个计数资源.相对于的要执行的释放计数资源的方法--->acquire(int permits)