一个最简单的实例理解Semaphore在Java中的作用

前言

阅读本篇文章,你需要先理解以下知识:

  • 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很好地解决了多线程死锁的问题,在未来的项目中也会经常地出现在各种情况中。

如转载请在文章尾部添加

原作者来自 adlered 个人技术博客:https://www.stackoverflow.wiki/

    评论
    1 评论
    nix
    2020-09-03 23:51 回复»

    可以

avatar

取消