前言
阅读本篇文章,你需要先理解以下知识:
- Java基础知识
- Thread多线程(点我跳转)
- 击鼓传花的玩法
你有没有和小伙伴们玩过击鼓传花
这个游戏?多个人同时只有“一朵花”的情况下,只有一个人最后会“中奖”。Semaphore
就像击鼓手一样控制着这朵“花”究竟“花落谁家”。
当然了,Semaphore
不只支持调度一朵花。
它在Java中常被用于线程的调度,当有多个线程访问同一个资源时,我们可以让线程尝试从Semaphore
获取一个许可证,如果该线程尝试访问的对象正在被其它线程占用,该线程将无法获取许可证,即循环等待重新获取。
Semaphore并不难理解,请不要被下面一大串代码所劝退,认真试验下去。
拷贝
等等! 在拷贝之前你要知道,下面的代码有3中测试模式,将test的值改为1/2/3会开启不同的代码段,它们的难度是不一样的。
好的。现在打开你的IDE,新建一个项目或类,将类命名为SemaphoreTest
,并将下面的代码替换到你的项目中:
1import java.util.concurrent.Semaphore;
2import java.util.concurrent.TimeUnit;
3
4public class SemaphoreTest {
5 public static void main(String[] args) {
6 //实例化Semaphore,并设置许可证为1个
7 Semaphore semaphore = new Semaphore(1);
8 //将test改为2或3,可以开启另一个测试
9 /**
10 * test为1时:验证许可证是否生效,难度1
11 * test为2时:验证释放许可证是否生效,难度2
12 * test为3时:多线程验证timeout是否生效,难度3
13 */
14 int test = 1;
15 if (test == 1) {
16 try {
17
18 //尝试在一秒内获取一个许可证
19 if (semaphore.tryAcquire(1, TimeUnit.SECONDS)) {
20 System.out.println("甲:获取许可证成功。");
21 } else {
22 System.out.println("甲:获取许可证失败。");
23 }
24 //尝试再次获取一个许可证
25 if (semaphore.tryAcquire(1, TimeUnit.SECONDS)) {
26 System.out.println("乙:获取许可证成功。");
27 } else {
28 System.out.println("乙:获取许可证失败。");
29 }
30
31
32 } catch (Exception e) {
33 e.printStackTrace();
34 }
35
36 } else if (test == 2) {
37
38 try {
39
40 //尝试在一秒内获取一个许可证
41 if (semaphore.tryAcquire(1, TimeUnit.SECONDS)) {
42 System.out.println("甲:获取许可证成功。");
43 //延迟五秒,然后使用release()方法释放该许可证
44 System.out.println("甲:五秒后释放许可证。");
45 Thread.sleep(5000);
46 //释放许可证
47 System.out.println("甲:许可证已释放。");
48 semaphore.release();
49 } else {
50 System.out.println("甲:获取许可证失败。");
51 }
52 //尝试再次获取一个许可证
53 if (semaphore.tryAcquire(1, TimeUnit.SECONDS)) {
54 System.out.println("乙:获取许可证成功。");
55 } else {
56 System.out.println("乙:获取许可证失败。");
57 }
58
59
60 } catch (Exception e) {
61 e.printStackTrace();
62 }
63
64 } else if (test == 3) {
65
66 new Thread(new Runnable() {
67 @Override
68 public void run() {
69 try {
70
71
72 //尝试在一秒内获取一个许可证
73 if (semaphore.tryAcquire(1, TimeUnit.SECONDS)) {
74 System.out.println("甲:获取许可证成功。");
75 //延迟五秒,然后使用release()方法释放该许可证
76 System.out.println("甲:五秒后释放许可证。");
77 Thread.sleep(5000);
78 //释放许可证
79 System.out.println("甲:许可证已释放。");
80 semaphore.release();
81 } else {
82 System.out.println("甲:获取许可证失败。");
83 }
84
85
86 } catch (Exception e) {
87 e.printStackTrace();
88 }
89 }
90 }).start();
91
92 new Thread(new Runnable() {
93 @Override
94 public void run() {
95 try {
96
97 //尝试再次获取一个许可证
98 System.out.println("乙:正在获取许可证,超时为:10秒。");
99 if (semaphore.tryAcquire(10, TimeUnit.SECONDS)) {
100 System.out.println("乙:获取许可证成功。");
101 } else {
102 System.out.println("乙:获取许可证失败。");
103 }
104
105 } catch (Exception e) {
106 e.printStackTrace();
107 }
108 }
109 }).start();
110
111 }
112 }
113}
运行!
现在,运行你的代码,你会发现结果如下:
1甲:获取许可证成功。
2乙:获取许可证失败。
这是因为甲占用了许可证但并未释放,而乙尝试获取许可证时超时被设置为了1秒,在1秒内无法成功获取许可证,所以获取失败。
将变量test
的值改为2,再次运行:
1甲:获取许可证成功。
2甲:五秒后释放许可证。
3甲:许可证已释放。
4乙:获取许可证成功。
可以看到甲在5秒后释放了许可证,所以乙成功地获取了许可证。
将变量test
的值改为3,再次运行:
1甲:获取许可证成功。
2乙:正在获取许可证,超时为:10秒。
3甲:五秒后释放许可证。
4甲:许可证已释放。
5乙:获取许可证成功。
本段代码我采用了多线程,使得甲和乙同时运行。乙的超时设置为了10秒,而甲在5秒后便释放了许可证,所以乙在5秒时也成功获取了许可证,并输出成功信息。
后语
Semaphore很好地解决了多线程死锁的问题,在未来的项目中也会经常地出现在各种情况中。