这是我们将要进行的Java并发系列的第一部分。 具体来说,我们将深入探讨Java 1.5及更高版本中内置的并发工具。 我们假设您对同步和易失性关键字有基本的了解。

第一篇文章将介绍信号量-特别是对信号量进行计数 。 信号量是用于限制对资源访问的经常被误解和使用不足的工具。 对于其他控制对资源的访问的方式,它们将被忽略。 但是信号量为我们提供了一个超越常规同步和其他工具所能提供的工具集的工具集。

那么什么是信号量? 想到信号量的最简单方法是将其视为允许n个单位被获取并提供获取和释放机制的抽象。 它安全地允许我们确保在给定的时间只有n个进程可以访问特定资源

一切都很好,但是这将达到什么目的呢? 好吧,这是一个示例,将有助于解释其用法。 它使用位于1.5。中的java.util.concurrent包中精心设计的Semaphore类。

限制连接

也许我们有一个过程可以通过HTTP定期为我们下载资源。 我们不想向任何主机发送垃圾邮件,同时,我们想限制正在建立的连接数,因此我们不会耗尽允许的有限文件句柄或出站连接。 一种简单的方法是使用信号量:

public class ConnectionLimiter {private final Semaphore semaphore;private ConnectionLimiter(int maxConcurrentRequests) {semaphore = new Semaphore(maxConcurrentRequests);}public URLConnection acquire(URL url) throws InterruptedException,IOException {semaphore.acquire();return url.openConnection();}public void release(URLConnection conn) {try {/** ... clean up here*/} finally {semaphore.release();}}
}

对于资源有限的问题,这是一个很好的解决方案。 对acquire()的调用将阻塞,直到获得许可为止。 信号灯的优点在于,它隐藏了管理访问控制,计算许可数以及确保正确的线程安全性的所有复杂性。

危险性

与大多数锁定或同步方法一样,存在一些潜在问题。

要记住的第一件事是, 始终释放您获得的东西 。 这是通过使用try..finally构造完成的。

使用信号量时,还有其他不太明显的问题可能会降临您。 以下课程显示了死锁,只有您中最幸运的人才能避免。 您会注意到,获得两个信号量许可的两个线程的执行顺序相反。 (为简洁起见,try..finally最终被省去了)。

public static void main(String[] args) throws Exception {Semaphore s1 = new Semaphore(1);Semaphore s2 = new Semaphore(1);Thread t = new Thread(new DoubleResourceGrabber(s1, s2));// now reverse them ... here comes trouble!Thread t2 = new Thread(new DoubleResourceGrabber(s2, s1));t.start();t2.start();t.join();t2.join();System.out.println("We got lucky!");
}private static class DoubleResourceGrabber implements Runnable {private Semaphore first;private Semaphore second;public DoubleResourceGrabber(Semaphore s1, Semaphore s2) {first = s1;second = s2;}public void run() {try {Thread t = Thread.currentThread();first.acquire();System.out.println(t + " acquired " + first);Thread.sleep(200); // demonstrate deadlocksecond.acquire();System.out.println(t + " acquired " + second);second.release();System.out.println(t + " released " + second);first.release();System.out.println(t + " released " + first);} catch (InterruptedException ex) {ex.printStackTrace();}}
}

如果运行此程序,则很有可能会挂起一个进程。 锁排序的问题与Java中的常规互斥锁或同步一样,也适用于信号量。 在某些情况下,超时(请参阅本文后面的tryAcquire()注释)可用于防止死锁导致进程挂起,但是死锁通常是可以避免的逻辑错误的征兆。 如果您不熟悉死锁,建议您仔细阅读它们。 维基百科上有一篇关于死锁的文章,该文章同样适用于所有语言。

使用信号量(包括二进制信号量,即互斥体)时应注意的主要事项是:

  • 获取后不释放(丢失的释放调用或引发异常并且没有finally块)
  • 长时间保持信号量,导致线程饥饿
  • 死锁(如上所示)

有用的信号灯技巧

Java中Semaphores的一个有趣的特性是, 不必通过与Acquisition相同的线程来调用release 。 这意味着您可以具有一个线程限制器,该线程限制器可以通过调用acquire()来基于信号量池或创建线程。 然后,正在运行的线程可以在完成时释放其自己的信号灯许可。 这是Java中普通互斥锁所没有的有用属性。

另一个技巧是在运行时增加许可数量 。 与您可能会猜到的相反,信号量中的许可数量不是固定的,并且即使未进行相应的acquire()调用,对release()的调用也会始终增加许可的数量。 请注意,如果在没有进行acquire()的情况下错误地调用release() ,这也会导致错误。

最后,在Java的Semaphore中有一些有用的方法要熟悉。 方法AcquireInterruptible()将获取资源,如果资源被中断,则重新尝试。 这意味着没有对InterruptedException的外部处理。 tryAcquire()方法允许我们限制等待许可的时间-我们可以在没有许可的情况下立即返回,也可以等待指定的超时时间。 如果您以某种方式知道了无法轻松修复或跟踪的死锁,则可以通过使用带有适当超时的tryAcquire()来帮助防止锁定进程。

用途

计数信号量有哪些可能的用途? 请注意以下几点:

  • 限制对磁盘的并发访问(由于竞争磁盘搜寻,这可能会降低性能)
  • 线程创建限制
  • JDBC连接池/限制
  • 网络连接限制
  • 限制CPU或内存密集型任务

当然,信号量是访问控制和同步的一个很底层的构建块。 Java为我们提供了Java 1.5及更高版本中引入的大量并发机制和策略。 在接下来的文章中,我们将介绍一些更抽象的并发管理方法,包括执行器,BlockingQueues,Barriers,Future以及一些新的并发Collection类。

您发现信号量有什么用途? 通过发表评论让我们知道–我们喜欢语音软件。

参考: Java并发第1部分–来自我们的JCG合作伙伴的信号灯 ,在Carfey Software博客上 。

相关文章 :
  • Java并发教程–重入锁
  • Java并发教程–线程池
  • Java并发教程–可调用,将来
  • Java并发教程–阻塞队列
  • Java并发教程– CountDownLatch
  • Exchanger和无GC的Java
  • Java Fork / Join进行并行编程
  • Java最佳实践–队列之战和链接的ConcurrentHashMap
  • 如何在不到1ms的延迟内完成100K TPS
  • 使用迭代器时如何避免ConcurrentModificationException
  • 改善Java应用程序性能的快速技巧
相关片段:
  • 阻塞队列示例以执行命令
  • 限制URL连接的信号量示例
  • 执行命令的同步队列示例
  • 更一般的等待/通知机制的CountDownLatch示例

翻译自: https://www.javacodegeeks.com/2011/09/java-concurrency-tutorial-semaphores.html

Java并发教程–信号量相关推荐

  1. Java并发教程– CountDownLatch

    Java中的某些并发实用程序自然会比其他并发实用程序受到更多关注,因为它们可以解决通用问题而不是更具体的问题. 我们大多数人经常遇到执行程序服务和并发集合之类的事情. 其他实用程序不太常见,因此有时它 ...

  2. Java并发教程–阻塞队列

    如第3部分所述,Java 1.5中引入的线程池提供了核心支持,该支持很快成为许多Java开发人员的最爱. 在内部,这些实现巧妙地利用了Java 1.5中引入的另一种并发功能-阻塞队列. 队列 首先,简 ...

  3. Java并发教程–可调用,将来

    从Java的第一个发行版开始,Java的美丽之处之一就是我们可以轻松编写多线程程序并将异步处理引入我们的设计中. Thread类和Runnable接口与Java的内存管理模型结合使用,意味着可以进行简 ...

  4. Java并发教程–重入锁

    Java的synced关键字是一个很棒的工具–它使我们能够以一种简单可靠的方式来同步对关键部分的访问,而且也不难理解. 但是有时我们需要对同步进行更多控制. 我们要么需要分别控制访问类型(读取和写入) ...

  5. Java并发教程–线程池

    Java 1.5中提供的最通用的并发增强功能之一是引入了可自定义的线程池. 这些线程池使您可以对诸如线程数,线程重用,调度和线程构造之类的东西进行大量控制. 让我们回顾一下. 首先,线程池. 让我们直 ...

  6. java 并发 同步信号_Java并发教程–信号量

    java 并发 同步信号 这是我们将要进行的Java并发系列的第一部分. 具体来说,我们将深入研究Java 1.5及更高版本中内置的并发工具. 我们假设您对同步和易失性关键字有基本的了解. 第一篇文章 ...

  7. Java并发教程(Oracle官方资料)

    2019独角兽企业重金招聘Python工程师标准>>> 本文是Oracle官方的Java并发相关的教程,感谢并发编程网的翻译和投递. (关注ITeye官微,随时随地查看最新开发资讯. ...

  8. oracle java 并发_【转】JAVA并发教程(ORACLE官网资料)

    本文是Oracle官方的Java并发相关的教程,感谢并发编程网的翻译和投递. 计算机的使用者一直以为他们的计算机可以同时做很多事情.他们认为当其他的应用程序在下载文件,管理打印队列或者缓冲音频的时候他 ...

  9. Java并发教程–锁定:显式锁定

    1.简介 在许多情况下,使用隐式锁定就足够了. 有时,我们将需要更复杂的功能. 在这种情况下, java.util.concurrent.locks包为我们提供了锁定对象. 当涉及到内存同步时,这些锁 ...

最新文章

  1. 每一个C#开发者必须知道的13件事情
  2. 在什么场景下该使用JMX,如何使用,会带来什么好处,这种场景下有哪些替代方案?...
  3. 文件服务器高可用群集,fastDFS文件服务器(三):集群和高可用环境篇
  4. python去掉列表的括号和逗号_在每行之后显示没有逗号,括号等的python 2d...
  5. DRAM,SRAM,SDRAM的关系与区别
  6. 将session维护在客户端
  7. 两年前搭建的网狐系统
  8. PKM(个人知识管理)类软件收集(偶尔更新列表)
  9. vi通过Vundle安装和删除插件
  10. WebSocket详解(一):初步认识WebSocket技术
  11. 【面试题】序列化的 10 几个问题
  12. Enterprise Library 3.1 简化使用范例一
  13. C++ list 函数用法整理
  14. opencv图像分析与处理(5)- 取样和取样函数的傅立叶变换
  15. rocketmq顺序消费问题
  16. 鲁棒控制(棒棒控制原理)
  17. mysql使用命令行导入sql脚本 报错无法插入中文
  18. Introduction to CMake by Example
  19. 人工智能时代对会计行业的改变与反思
  20. ABB KUC321AE HIEE300698R0001、KUC755AE106 3BHB005243R0106、KUC711AE 3BHB004661R0001、KUC720AE01

热门文章

  1. Post请求如何取消异步
  2. mysql中使用CASE WHEN
  3. 托管 非托管_如何在托管Kubernetes上还原Neo4J备份
  4. reactor使用方法_Project Reactor展开方法
  5. spock测试_使用Spock测试您的代码
  6. apache camel_学习Apache Camel –实时索引推文
  7. javafx 剪切板_JavaFX技巧18:路径剪切
  8. jooq 执行sql_使用jOOQ和Java 8的CompletableFuture进行异步SQL执行
  9. Java中的PriorityBlockingQueue
  10. JavaFX技巧30:带有DropShadow的ScrollPane