Semaphore

  • 简介
  • 常用方法
  • 示例


简介

多用于线程多于资源下的资源控制的线程控制器。很像锁同步,和锁同步不同的是锁锁定一个资源,同时只能有一个线程操作这个资源。而Semaphore则是锁定一批资源。同时只允许指定数目的线程执行操作。
也与线程池有一定相似。不同的是线程池等待的线程未运行,而这个线程已经在运行并且在争夺。
适用于资源量小于线程量的情况。比如一个连接只允许同时有10个线程连接,现在有100个线程要执行这个连接操作,那么就可以用Semaphore进行控制。

常用方法

  • Semaphore(int permits):构造方法,入参为许可量,即同时允许几个线程动作。默认为非公平的
  • Semaphore(int permits, boolean fair):构造方法入参一为许可量,入参二为是否公平竞争 true为公平
  • void acquire() throws InterruptedException:去获得一个许可,获得不到就一直等。等待过程中被中断报InterruptedException
  • void acquireUninterruptibly():去获得一个许可,获得不到就一直等,无视中断命令
  • boolean tryAcquire(): 尝试获得一个许可,如果获得就返回true,没获得就返回false
  • boolean tryAcquire(long timeout, TimeUnit unit): 在指定时间里尝试获得一个许可,如果获得就返回true,没获得就返回false
  • void release(): 释放一个许可,无需一定持有许可。即没有持有许可依然可以发出许可释放指令
  • void acquire(int permits) throws InterruptedException:去获得指定数目的许可,获得不到就一直等。等待过程中被中断报InterruptedException
  • void acquireUninterruptibly(int permits):去获得指定数目的许可,获得不到就一直等,无视中断命令
  • boolean tryAcquire(int permits):去获得指定数目的许可,如果获得就返回true,没获得就返回false
  • boolean tryAcquire(int permits, long timeout, TimeUnit unit):在指定时间里尝试获得指定数目的许可,如果获得就返回true,没获得就返回false
  • void release(int permits): 释放指定数目的许可,无需一定持有指定数目许可。即使未拥有许可或者只拥有一个许可,依然可以释放两个甚至更多的许可。
  • int availablePermits(): 当前可用许可量
  • int drainPermits():获得所有的许可并返回获得的许可量
  • boolean isFair():是否为公平竞争
  • final boolean hasQueuedThreads():当前是否有线程在竞争
  • final int getQueueLength():当前竞争队列的长度

示例

package sync;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Semaphore;

public class SemaphoreTest {

    public static void main(String[] args) {
        final Semaphore s = new Semaphore(2);  // 入参为2则为正常的3任务调2个许可流程,为1可验证无需获得许可便可释放许可
        Runnable r1 = new Runnable() {
            
            @Override
            public void run() {
                try {
                    System.out.println("1号请求资源 - " + showNow());
                    s.acquire();
                    System.out.println("1号获得资源 - " + showNow());
                    System.out.println("1号开始工作 - " + showNow());
                    Thread.sleep(5000);
                    System.out.println("1号工作完毕,开始释放资源 - " + showNow());
                    s.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        
        Runnable r2 = new Runnable() {
            
            @Override
            public void run() {
                try {
//                    Thread.sleep(200);  // 保证执行顺序是1.2.3
                    System.out.println("2号请求资源 - " + showNow());
                    s.acquire();  // 如果把这个地方注释掉,并打开保证释放顺序的代码并设置许可为一个,运行会发现1号先持有资源工作,然后2无需获得许可就可释放许可,使得3在1未执行完毕释放许可时便获得许可开始执行
                    System.out.println("2号获得资源 - " + showNow());
                    System.out.println("2号开始工作 - " + showNow());
                    Thread.sleep(1000);
                    System.out.println("2号工作完毕,开始释放资源 - " + showNow());
                    s.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        
        Runnable r3 = new Runnable() {
            
            @Override
            public void run() {
                try {
//                    Thread.sleep(500);// 保证执行顺序是1.2.3
                    System.out.println("3号请求资源 - " + showNow());
                    s.acquire();
                    System.out.println("3号获得资源 - " + showNow());
                    System.out.println("3号开始工作 - " + showNow());
                    Thread.sleep(1000);
                    System.out.println("3号工作完毕,开始释放资源 - " + showNow());
                    s.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        
        new Thread(r1).start();
        new Thread(r2).start();
        new Thread(r3).start();
    }

    private static String showNow(){
        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
        return sdf.format(new Date());
    }
    
}

运行结果:

2号请求资源 - 17:23:48.107
1号请求资源 - 17:23:48.107
2号获得资源 - 17:23:48.107
3号请求资源 - 17:23:48.107
2号开始工作 - 17:23:48.107
1号获得资源 - 17:23:48.107
1号开始工作 - 17:23:48.107
2号工作完毕,开始释放资源 - 17:23:49.107
3号获得资源 - 17:23:49.108
3号开始工作 - 17:23:49.108
3号工作完毕,开始释放资源 - 17:23:50.108
1号工作完毕,开始释放资源 - 17:23:53.107