Java并发之Semaphore

来源:互联网 发布:知乎二战德国石油 编辑:程序博客网 时间:2024/06/05 17:30

    Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。

一、方法摘要

  • Semaphore(int permits):构造方法,创建具有给定的许可数和非公平锁的Semaphore。
  • Semaphore(int permits, Boolean fair):构造方法,创建具有给定许可数和给定公平设置的Semaphore。
  • void acquire():从此信号量获取一个许可,在提供一个许可前将阻塞,否则线程被中断。
  • void acquire(int permits):从此信号量获取给定数目的许可,在提供这些许可前阻塞,或者线程已被中断。
  • void acquireUninterruptibly():获取许可,无视中断。
  • Boolean tryAcquire():仅在调用此信号量时存在一个许可,才从信号量获取许可。
  • void release():释放一个许可,将其返回给信号量。

二、使用方法

    Semaphore是一个计数信号量。从概念上讲,信号量维护了一个许可集。如有必要,在许可可用前会阻塞每一个acquire(),然后再获取许可。每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。

Semaphore 通常用于限制可以访问某些资源(物理或逻辑的)的线程数目。例如,下面的类使用信号量控制对内容池的访问:

public class SemaphoreTest {private static final int THREAD_COUNT = 30;private static ExecutorService threadPool = Executors.newFixedThreadPool(THREAD_COUNT);private static Semaphore s = new Semaphore(10);public static void main(String[] args) {for (int i = 0; i < THREAD_COUNT; i++) {threadPool.execute(new Runnable() {@Overridepublic void run() {try {s.acquire();//获取许可System.out.println("save data");s.release();//释放许可} catch (InterruptedException e) {}}});}threadPool.shutdown();}}

    在代码中,虽然有30个线程在执行,但是只允许10个并发的执行。Semaphore的构造方法Semaphore(int permits) 接受一个整型的数字,表示可用的许可证数量。Semaphore(10)表示允许10个线程获取许可证,也就是最大并发数是10。Semaphore的用法也很简单,首先线程使用Semaphore的acquire()获取一个许可证,使用完之后调用release()归还许可证。还可以用tryAcquire()方法尝试获取许可证。

三、实现原理

    结构类图

    信号量内含了两个队列同步器,分别是公平的和非公平的。通过队列同步器来控制线程同步。

    构造函数

    public Semaphore(int permits, boolean fair) {        sync = fair ? new FairSync(permits) : new NonfairSync(permits);    }

    Semaphore内置了两个队列同步器,分别为公平的和非公平的。根据构造参数的不同,决定实现哪一个同步器。

    acquire 方法

    acquire()方法用来获取许可,这里的许可其实关联到队列同步器AQS的状态state。源码如下:

    public void acquire() throws InterruptedException {        sync.acquireSharedInterruptibly(1);//获取共享锁    }

    AQS中,该方法的实现为:

    public final void acquireSharedInterruptibly(int arg)            throws InterruptedException {        if (Thread.interrupted())            throw new InterruptedException();        if (tryAcquireShared(arg) < 0)//尝试获取同步            doAcquireSharedInterruptibly(arg);//尝试失败 则 继续等待    }

    该方法为模板方法,需要对tryAcquireShared方法进行重写。对于非公平锁,源码如下:

        protected int tryAcquireShared(int acquires) {            return nonfairTryAcquireShared(acquires);//调用了父类方法        }
        final int nonfairTryAcquireShared(int acquires) {            for (;;) {                int available = getState();//获取状态                int remaining = available - acquires;//更新状态                if (remaining < 0 ||                    compareAndSetState(available, remaining))                    return remaining;//返回新的状态值            }        }

    对于公平锁,则需要加上判断,该线程是否为同步队列的头结点才可。acquire本质上是交出同步器的state给线程。当state消耗完时(<=0)就无法再获得共享锁。

    release 方法

    release方法用来归还信号量。源码如下:

    public void release() {        sync.releaseShared(1);//调用同步器的释放共享锁    }

    该方法调用了同步器的模板方法:

    public final boolean releaseShared(int arg) {        if (tryReleaseShared(arg)) {//尝试释放锁            doReleaseShared();            return true;        }        return false;    }
    在Semaphore中,重写了tryReleaseShared方法:

        protected final boolean tryReleaseShared(int releases) {            for (;;) {                int current = getState();//获取state值                int next = current + releases;//归还信号量                if (next < current) // overflow                    throw new Error("Maximum permit count exceeded");                if (compareAndSetState(current, next))//更新state                    return true;            }        }


       
   



原创粉丝点击