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

本贴最后更新于 2198 天前,其中的信息可能已经时移世改

v27f385d4edfa77ffefff75c4be731a855hd.jpg

前言

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

  • Java 基础知识
  • Thread 多线程(点我跳转
  • 击鼓传花的玩法

你有没有和小伙伴们玩过 击鼓传花 这个游戏?多个人同时只有“一朵花”的情况下,只有一个人最后会“中奖”。Semaphore 就像击鼓手一样控制着这朵“花”究竟“花落谁家”。

当然了,Semaphore 不只支持调度一朵花。

它在 Java 中常被用于线程的调度,当有多个线程访问同一个资源时,我们可以让线程尝试从 Semaphore 获取一个许可证,如果该线程尝试访问的对象正在被其它线程占用,该线程将无法获取许可证,即循环等待重新获取。

Semaphore 并不难理解,请不要被下面一大串代码所劝退,认真试验下去。

拷贝

等等! 在拷贝之前你要知道,下面的代码有 3 中测试模式,将 test 的值改为 1/2/3 会开启不同的代码段,它们的难度是不一样的。

好的。现在打开你的 IDE,新建一个项目或类,将类命名为 SemaphoreTest,并将下面的代码替换到你的项目中:

import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; public class SemaphoreTest { public static void main(String[] args) { //实例化Semaphore,并设置许可证为1个 Semaphore semaphore = new Semaphore(1); //将test改为2或3,可以开启另一个测试 /** * test为1时:验证许可证是否生效,难度1 * test为2时:验证释放许可证是否生效,难度2 * test为3时:多线程验证timeout是否生效,难度3 */ int test = 1; if (test == 1) { try { //尝试在一秒内获取一个许可证 if (semaphore.tryAcquire(1, TimeUnit.SECONDS)) { System.out.println("甲:获取许可证成功。"); } else { System.out.println("甲:获取许可证失败。"); } //尝试再次获取一个许可证 if (semaphore.tryAcquire(1, TimeUnit.SECONDS)) { System.out.println("乙:获取许可证成功。"); } else { System.out.println("乙:获取许可证失败。"); } } catch (Exception e) { e.printStackTrace(); } } else if (test == 2) { try { //尝试在一秒内获取一个许可证 if (semaphore.tryAcquire(1, TimeUnit.SECONDS)) { System.out.println("甲:获取许可证成功。"); //延迟五秒,然后使用release()方法释放该许可证 System.out.println("甲:五秒后释放许可证。"); Thread.sleep(5000); //释放许可证 System.out.println("甲:许可证已释放。"); semaphore.release(); } else { System.out.println("甲:获取许可证失败。"); } //尝试再次获取一个许可证 if (semaphore.tryAcquire(1, TimeUnit.SECONDS)) { System.out.println("乙:获取许可证成功。"); } else { System.out.println("乙:获取许可证失败。"); } } catch (Exception e) { e.printStackTrace(); } } else if (test == 3) { new Thread(new Runnable() { @Override public void run() { try { //尝试在一秒内获取一个许可证 if (semaphore.tryAcquire(1, TimeUnit.SECONDS)) { System.out.println("甲:获取许可证成功。"); //延迟五秒,然后使用release()方法释放该许可证 System.out.println("甲:五秒后释放许可证。"); Thread.sleep(5000); //释放许可证 System.out.println("甲:许可证已释放。"); semaphore.release(); } else { System.out.println("甲:获取许可证失败。"); } } catch (Exception e) { e.printStackTrace(); } } }).start(); new Thread(new Runnable() { @Override public void run() { try { //尝试再次获取一个许可证 System.out.println("乙:正在获取许可证,超时为:10秒。"); if (semaphore.tryAcquire(10, TimeUnit.SECONDS)) { System.out.println("乙:获取许可证成功。"); } else { System.out.println("乙:获取许可证失败。"); } } catch (Exception e) { e.printStackTrace(); } } }).start(); } } }

运行!

现在,运行你的代码,你会发现结果如下:

甲:获取许可证成功。 乙:获取许可证失败。

这是因为甲占用了许可证但并未释放,而乙尝试获取许可证时超时被设置为了 1 秒,在 1 秒内无法成功获取许可证,所以获取失败。

将变量 test 的值改为 2,再次运行:

甲:获取许可证成功。 甲:五秒后释放许可证。 甲:许可证已释放。 乙:获取许可证成功。

可以看到甲在 5 秒后释放了许可证,所以乙成功地获取了许可证。

将变量 test 的值改为 3,再次运行:

甲:获取许可证成功。 乙:正在获取许可证,超时为:10秒。 甲:五秒后释放许可证。 甲:许可证已释放。 乙:获取许可证成功。

本段代码我采用了多线程,使得甲和乙同时运行。乙的超时设置为了 10 秒,而甲在 5 秒后便释放了许可证,所以乙在 5 秒时也成功获取了许可证,并输出成功信息。

后语

Semaphore 很好地解决了多线程死锁的问题,在未来的项目中也会经常地出现在各种情况中。

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3194 引用 • 8214 回帖
  • 代码
    467 引用 • 586 回帖 • 9 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
adlered
Java 开发业余爱好者,业余开源爱好者