惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Project Zero
Project Zero
GbyAI
GbyAI
Y
Y Combinator Blog
Google DeepMind News
Google DeepMind News
小众软件
小众软件
The GitHub Blog
The GitHub Blog
阮一峰的网络日志
阮一峰的网络日志
J
Java Code Geeks
WordPress大学
WordPress大学
Microsoft Security Blog
Microsoft Security Blog
IT之家
IT之家
F
Fortinet All Blogs
博客园 - 【当耐特】
H
Hackread – Cybersecurity News, Data Breaches, AI and More
P
Proofpoint News Feed
Schneier on Security
Schneier on Security
C
Cybersecurity and Infrastructure Security Agency CISA
L
LINUX DO - 热门话题
Spread Privacy
Spread Privacy
O
OpenAI News
V
V2EX
博客园 - 三生石上(FineUI控件)
AI
AI
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Stack Overflow Blog
Stack Overflow Blog
P
Privacy International News Feed
Microsoft Azure Blog
Microsoft Azure Blog
Blog — PlanetScale
Blog — PlanetScale
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Jina AI
Jina AI
H
Help Net Security
L
LINUX DO - 最新话题
Application and Cybersecurity Blog
Application and Cybersecurity Blog
T
Tenable Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Cisco Talos Blog
Cisco Talos Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
博客园 - 叶小钗
V
Vulnerabilities – Threatpost
C
Cisco Blogs
D
Darknet – Hacking Tools, Hacker News & Cyber Security
酷 壳 – CoolShell
酷 壳 – CoolShell
Hacker News: Ask HN
Hacker News: Ask HN
S
Security @ Cisco Blogs
S
Securelist
T
The Blog of Author Tim Ferriss
Apple Machine Learning Research
Apple Machine Learning Research
美团技术团队
雷峰网
雷峰网
V2EX - 技术
V2EX - 技术

技术改变视野

为程序员、设计师打造的摸鱼社区:摸鱼派! - 技术改变视野 大白话 | Java初学指♂男:“说说初学的误区与死结”( 壹 | 反射与Field ) - 技术改变视野 大白话之耦合性:什么是耦合性和内聚性?用编程语言实例讲解! - 技术改变视野 🎨 适用于 Solo / Bolo 的清新高级主题 —— Sakura | 移植自 WordPress 主题 - 技术改变视野 我在 GitHub 上的开源项目 - 技术改变视野 为IDEA/Git配置GPG密钥,点亮GitHub Verified Commit标识 - 技术改变视野 从 SpringMVC 转为使用 SpringBoot —— 手把手带你使用 Intellij IDEA 最快速地搭建 SpringBoot+MyBatis(无 XML)+Thymeleaf - 技术改变视野 无题 - 技术改变视野 年轻人的第一辆京牌电动车 - 技术改变视野 我们忙碌且漫长的一生,有多久是为自己而活? - 技术改变视野 说说《后浪》 - 技术改变视野 离线使用 Google Fonts:让你的网站更精致 | goofoffline 神器,一键下载生成离线字体库! - 技术改变视野 实例带你学会简单的Java Thread多线程 - 技术改变视野 一个实例理解Java的接口(interface)用处与用法 - 技术改变视野 一个实例带你理解JavaBean - 技术改变视野 WebFilter-SpringBoot过滤器注解实例讲解 - 技术改变视野 实例带你搞懂Java多线程&&线程池之(壹):线程池与多线程的关系和区别 - 技术改变视野 实例带你搞懂Java多线程&&线程池之(贰):简单的线程池应用 - 技术改变视野 实例带你获取多线程Thread的返回值之 (壹) - Callable的运行 - 技术改变视野 实例带你获取多线程Thread的返回值之 (贰) - Callable配合线程池返回数据 - 技术改变视野 一个实例理解Java Runnable多线程用处与用法 - 技术改变视野 从零开始OpenSSL之 (壹) - 使用genrsa、rsa、rsautl生成公私钥 - 技术改变视野 从零开始 OpenSSL 之 (贰) - 使用 rsautl 解密文件 - 技术改变视野 大白话之Docker(壹):快速入门&&简单官方实例 - 技术改变视野 大白话之Docker(贰):简单部署一个Tomcat服务并发布内容 - 技术改变视野 大白话之从零讲解DVWA(壹)-SQL注入(SQL Injection) Low Level - 技术改变视野 大白话之从零讲解DVWA(贰)-SQL注入(SQL Injection) Medium/High Level - 技术改变视野 Java日志插件-Slog4J下载 - 技术改变视野 大白话之必会Java Atomic | 线程一点也不安全(一):比自增和synchronized更快速、靠谱的原子操作(调用C语言) - 技术改变视野 Log-MySQL root用户登录后无法查看数据库全部表/正常访问数据库 Access denied for user 'root'@'localhost' to database - 技术改变视野 大白话之Docker(叁):制作一个运行Tomcat服务端的Docker镜像 - 技术改变视野 Github仅保留指定文件/文件夹当前Commit,删除所有历史记录,清除「敏感信息」 - 技术改变视野 将Tomcat、MySQL从Linux迁移到Windows的心路历程(干货):令人恐惧的字符编码 - 技术改变视野 Spring: java.lang.NoSuchMethodError: clearCache | ClassNotFoundException | Error during artifact deployment 思路及解决办法 - 技术改变视野 大白话之Java反射-初学最迷的概念:能干啥?咋用? - 技术改变视野 大白话之Java面向接口编程:最“正经”的中文实例讲解,看不懂来打我! - 技术改变视野 大白话 | 课堂实践:使用Iterator对数组进行遍历 - 技术改变视野 大白话之fail-fast | fail-safe:为什么会有这个机制?它有什么作用? - 技术改变视野 刨根问底 | 大白话:在使用注解后,框架是怎么知道你哪个方法使用了注解的?用@RequestMapping注解举例详解! - 技术改变视野 大白话之必会Java Atomic | 线程一点也不安全(二):Atomic的ABA问题会导致什么情况?如何解决? - 技术改变视野 随笔 | 奇淫技巧 | Java:记return和短路运算符的妙用 - 技术改变视野 随笔 | Tomcat:续-从Linux迁移到Windows编码问题彻底解决 - 技术改变视野 大白话之AutoClosable接口 | try-with-resources:JDK1.7的新特性,提高重复回收效率 - 技术改变视野 大白话之Java多线程join方法:开局一张图,试学一分钟,你就费和我一样,理解介个方法 - 技术改变视野 大白话之Java Stream流:将类数组流化,便捷批量修改,通俗讲解! - 技术改变视野 Java 困扰三周の问题:使用byte[]或skip()方法读取字节流Stream文件尾部多/少/缺字节解决方法 - 技术改变视野 大白话之Java级联调用:一个类,一条语句,同时调用好几个方法,串成一串 - 技术改变视野
一个最简单的实例理解Semaphore在Java中的作用 - 技术改变视野
adlered · 2020-09-03 · via 技术改变视野

前言

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

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