信号量为多线程协作提供了更为强大的控制方法。广义上说,信号量是对锁的扩展。无论是内部锁synchronized还是重入锁ReentrantLock,一次都只允许一个线程访问一个资源,而信号量却可以指定多个线程,同时访问某一个资源。信号量主要提供了一下构造函数:

Semaphore(int permits) 
          Creates a Semaphore with the given number of permits and nonfair fairness setting.
Semaphore(int permits, boolean fair) 
          Creates a Semaphore with the given number of permits and the given fairness setting.第二个参数指定是否公平

  在构造信号量对象时,必须要指定信号量的准入数,即同时能申请多少个许可。当每个线程每次只申请一个许可时,这就相当于指定了同时有多少个线程可以访问某一个资源。信号量的主要逻辑方法有:

 void   acquire() 
          Acquires a permit from this semaphore, blocking until one is available, or the thread is interrupted.
 void   acquire(int permits) 
          Acquires the given number of permits from this semaphore, blocking until all are available, or the thread is interrupted.
 void   acquireUninterruptibly() 
          Acquires a permit from this semaphore, blocking until one is available.
 void   acquireUninterruptibly(int permits) 
          Acquires the given number of permits from this semaphore, blocking until all are available.
 int    availablePermits() 
          Returns the current number of permits available in this semaphore.
 int    drainPermits() 
          Acquires and returns all permits that are immediately available.
protected  Collection<Thread>   getQueuedThreads() 
          Returns a collection containing threads that may be waiting to acquire.
 int    getQueueLength() 
          Returns an estimate of the number of threads waiting to acquire.
 boolean    hasQueuedThreads() 
          Queries whether any threads are waiting to acquire.
 boolean    isFair() 
          Returns true if this semaphore has fairness set true.
protected  void reducePermits(int reduction) 
          Shrinks the number of available permits by the indicated reduction.
 void   release() 
          Releases a permit, returning it to the semaphore.
 void   release(int permits) 
          Releases the given number of permits, returning them to the semaphore.
 String toString() 
          Returns a string identifying this semaphore, as well as its state.
 boolean    tryAcquire() 
          Acquires a permit from this semaphore, only if one is available at the time of invocation.
 boolean    tryAcquire(int permits) 
          Acquires the given number of permits from this semaphore, only if all are available at the time of invocation.
 boolean    tryAcquire(int permits, long timeout, TimeUnit unit) 
          Acquires the given number of permits from this semaphore, if all become available within the given waiting time and the current thread has not been interrupted.
 boolean    tryAcquire(long timeout, TimeUnit unit) 
          Acquires a permit from this semaphore, if one becomes available within the given waiting time and the current thread has not been interrupted.

这里只讲几个常用的方法:

  • acquire() 方法尝试获得一个准入的许可。若无法获得,则线程会等待,直到有线程释放一个许可或者当前线程被中断。

  • acquireUninterruptibly方法和acquire方法类似,但是不响应中断。

  • tryAcquire尝试获得一个许可,如果成功返回true,失败返回false,它不会进行等待,立即返回。

  • release用于在线程访问资源结束后,释放一个许可,以使其他等待许可的线程可以进行资源访问。

一个案例:

/**
 * Created by niehongtao on 16/7/12.
 * 3.1.3允许多个线程同时访问:信号量
 */
public class SemapDemo implements Runnable {

    final Semaphore semp = new Semaphore(5);


    @Override
    public void run() {
        try {
            semp.acquire();
            // 模拟耗时操作
            Thread.sleep(5000);
            System.out.println(Thread.currentThread().getId() + ":done");
            semp.release();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }


    public static void main(String[] args) {
        ExecutorService exec = Executors.newFixedThreadPool(20);
        SemapDemo demo = new SemapDemo();
        for (int i = 0; i < 20; i++) {
            exec.execute(demo);
        }
    }
}

  上述代码中,15、16行为临界区,程序会限制执行这段代码的线程数。这里在第7行,声明了一个包含5个许可的信号量。这就意味着同时可以有5个线程进入代码段15,16行。申请信号量使用semp.acquire,在离开时,务必使用semp.release释放信号量。这就和释放锁一个道理。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐