Bootstrap

Java多线程工具包java.util.concurrent---Semaphore

什么是Semphore

java.util.concurrent.Semaphore 类是一个计数信号量。计数信号量由一个指定数量的 “许可” 初始化。每调用一次 acquire(),一个许可会被调用线程取走。每调用一次 release(),一个许可会被返还给信号量。因此,在没有任何 release() 调用时,最多有 N 个线程能够通过 acquire() 方法,N 是该信号量初始化时的许可的指定数量。这些许可只是一个简单的计数器。

主要常用方法

acquire()

 public void acquire()  throws InterruptedException

从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。获取一个许可(如果提供了一个)并立即返回,将可用的许可数减 1。如果没有可用的许可,则在发生以下两种情况之一前,禁止将当前线程用于线程安排目的并使其处于休眠状态

  • 某些其他线程调用此信号量的 release() 方法,并且当前线程是下一个要被分配许可的线程
  • 其他某些线程中断当前线程

这里说明一下红色字样的具体含义,当许可不够时,又有多个线程竞争许可,不能保证当前线程一定会是下一个被分配许可的线程。

没有办法保证线程能够公平地可从信号量中获得许可。也就是说,无法担保掉第一个调用 acquire() 的线程会是第一个获得一个许可的线程。如果第一个线程在等待一个许可时发生阻塞,而第二个线程前来索要一个许可的时候刚好有一个许可被释放出来,那么它就可能会在第一个线程之前获得许可。
如果你想要强制公平,Semaphore 类有一个具有一个布尔类型的参数的构造子,通过这个参数以告知 Semaphore 是否要强制公平。强制公平会影响到并发性能,所以除非你确实需要它否则不要启用它。

Semaphore semaphore = new Semaphore(1, true);  

release()

public void release()

释放一个许可,将其返回给信号量。释放一个许可,将可用的许可数增加 1。如果任意线程试图获取许可,则选中一个线程并将刚刚释放的许可给予它。然后针对线程安排目的启用(或再启用)该线程。

availablePermits()

public int availablePermits()

返回此信号量中当前可用的许可数

示例

这里还是利用之前提到的客服场景

package com.yvan.semaphore;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

/**
 * Semaphore
 * 
 * @author yvan
 *
 */
public class AppMain {
    // 客服数
    private static final int _SERVICER = 2;
    // 用户数
    private static final int _CUSTOMER = 10;

    public static void main(String[] args) throws Exception {
        Semaphore semaphore = new Semaphore(_SERVICER);
        ExecutorService executorService = Executors.newFixedThreadPool(_CUSTOMER);
        for (int i = 0; i < _CUSTOMER; i++) {
            executorService.execute(new Processer(semaphore, "客户" + i));
        }
        executorService.shutdown();
    }
}

class Processer implements Runnable {

    private Semaphore semaphore;
    private String user;

    public Processer(Semaphore semaphore, String user) {
        super();
        this.semaphore = semaphore;
        this.user = user;
    }

    @Override
    public void run() {
        try {
            // 获取当前许可
            // 场景中就是空闲的客服人员数
            int free = semaphore.availablePermits();
            if (free <= 0) {
                System.out.println("客服坐席正忙,请您耐心等待......");
            }
            // 获取许可,没有许可就等待
            semaphore.acquire();
            System.out.println(user + "已经接入,正在通话中......");
            // 模拟通话时长
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println(user + "通话结束......");
            // 释放许可
            semaphore.release();
        }

    }

}

结果

客户0已经接入,正在通话中……
客服坐席正忙,请您耐心等待……
客户1已经接入,正在通话中……
客服坐席正忙,请您耐心等待……
客服坐席正忙,请您耐心等待……
客服坐席正忙,请您耐心等待……
客服坐席正忙,请您耐心等待……
客服坐席正忙,请您耐心等待……
客服坐席正忙,请您耐心等待……
客服坐席正忙,请您耐心等待……
客户0通话结束……
客户1通话结束……
客户2已经接入,正在通话中……
客户3已经接入,正在通话中……
客户2通话结束……
客户4已经接入,正在通话中……
客户3通话结束……
客户5已经接入,正在通话中……
客户4通话结束……
客户6已经接入,正在通话中……
客户5通话结束……
客户7已经接入,正在通话中……
客户6通话结束……
客户8已经接入,正在通话中……
客户7通话结束……
客户9已经接入,正在通话中……
客户8通话结束……
客户9通话结束……

延伸示例

这里示例会给出一个秒杀场景,性能有待验证

package com.yvan.semaphore;

import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
 * Semaphore 秒杀
 * 
 * @author yvan
 *
 */
public class Seckill {
    // 秒杀商品数
    public static int _COUNT=7;
    // 并发秒杀用户数
    public static final int _CUSTOMER=10;
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(1);
        ExecutorService executorService = Executors.newFixedThreadPool(_CUSTOMER);
        for (int i = 0; i < _CUSTOMER; i++) {
            executorService.execute(new SeckillProcess(semaphore, "用户"+i));
        }
        executorService.shutdown();
    }
}

class SeckillProcess implements Runnable{

    private Semaphore semaphore ;
    private String user;



    public SeckillProcess(Semaphore semaphore, String user) {
        super();
        this.semaphore = semaphore;
        this.user = user;
    }



    @Override
    public void run() {
        try {
            // 模拟用户客户端网络连接情况
            TimeUnit.MILLISECONDS.sleep(new Random().nextInt(1000));
            semaphore.acquire();
            if (Seckill._COUNT>0) {
                Seckill._COUNT--;
                // 模拟后续秒杀业务执行时间
                TimeUnit.MILLISECONDS.sleep(100);
                System.out.println("恭喜"+user+"秒杀成功");
            }else {
                System.out.println(user+",非常遗憾,秒杀失败...");
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {

            semaphore.release();
        }

    }

}

结果

恭喜用户4秒杀成功
恭喜用户6秒杀成功
恭喜用户2秒杀成功
恭喜用户7秒杀成功
恭喜用户0秒杀成功
恭喜用户3秒杀成功
恭喜用户9秒杀成功
用户5,非常遗憾,秒杀失败…
用户1,非常遗憾,秒杀失败…
用户8,非常遗憾,秒杀失败…

;