Java 线程资源限制 java.util.concurrent.semaphore

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

主要常用方法

acquire()

1
public void acquire()  throws InterruptedException

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

某些其他线程调用此信号量的 release() 方法,并且当前线程是下一个要被分配许可的线程
其他某些线程中断当前线程
这里说明一下红色字样的具体含义,当许可不够时,又有多个线程竞争许可,不能保证当前线程一定会是下一个被分配许可的线程。

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

1
Semaphore semaphore = new Semaphore(1, true);

release()

1
public void release()

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

availablePermits()

1
public int availablePermits()

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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通话结束……

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
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,非常遗憾,秒杀失败…