热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Java并发编程指南:深入理解信号量机制

本文是Java并发编程系列的开篇之作,将详细解析Java1.5及以上版本中提供的并发工具。文章假设读者已经具备同步和易失性关键字的基本知识,重点介绍信号量机制的内部工作原理及其在实际开发中的应用。
这是我们将要进行的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高并发与多线程(二):线程的实现方式详解
    本文将深入探讨Java中线程的三种主要实现方式,包括继承Thread类、实现Runnable接口和实现Callable接口,并分析它们之间的异同及其应用场景。 ... [详细]
  • 如何利用Java 5 Executor框架高效构建和管理线程池
    Java 5 引入了 Executor 框架,为开发人员提供了一种高效管理和构建线程池的方法。该框架通过将任务提交与任务执行分离,简化了多线程编程的复杂性。利用 Executor 框架,开发人员可以更灵活地控制线程的创建、分配和管理,从而提高服务器端应用的性能和响应能力。此外,该框架还提供了多种线程池实现,如固定线程池、缓存线程池和单线程池,以适应不同的应用场景和需求。 ... [详细]
  • 零拷贝技术是提高I/O性能的重要手段,常用于Java NIO、Netty、Kafka等框架中。本文将详细解析零拷贝技术的原理及其应用。 ... [详细]
  • 本文介绍了 .NET 中用于线程间通信的工具 WaitHandle 及其子类 ManualResetEvent 和 AutoResetEvent,并详细解释了线程池的概念及其在优化资源利用方面的优势。 ... [详细]
  • 本文总结了一些开发中常见的问题及其解决方案,包括特性过滤器的使用、NuGet程序集版本冲突、线程存储、溢出检查、ThreadPool的最大线程数设置、Redis使用中的问题以及Task.Result和Task.GetAwaiter().GetResult()的区别。 ... [详细]
  • 深入解析 Synchronized 锁的升级机制及其在并发编程中的应用
    深入解析 Synchronized 锁的升级机制及其在并发编程中的应用 ... [详细]
  • 性能测试中的关键监控指标与深入分析
    在软件性能测试中,关键监控指标的选取至关重要。主要目的包括:1. 评估系统的当前性能,确保其符合预期的性能标准;2. 发现软件性能瓶颈,定位潜在问题;3. 优化系统性能,提高用户体验。通过综合分析这些指标,可以全面了解系统的运行状态,为后续的性能改进提供科学依据。 ... [详细]
  • 构建基础的字符串队列实现方法
    在探讨如何构建基础的字符串队列实现方法时,我们发现许多开发者在面对这一问题时常常感到困惑。实际上,队列的基本原理非常简单,即遵循先进先出的原则。然而,在具体实现过程中,需要注意的是Java语言中并没有指针的概念,因此需要通过嵌套类来模拟指针,进而构建链表结构。这种实现方式不仅能够有效地管理字符串数据,还能提升代码的可读性和维护性。 ... [详细]
  • 在Java项目中,当两个文件进行互相调用时出现了函数错误。具体问题出现在 `MainFrame.java` 文件中,该文件位于 `cn.javass.bookmgr` 包下,并且导入了 `java.awt.BorderLayout` 和 `java.awt.Event` 等相关类。为了确保项目的正常运行,请求提供专业的解决方案,以解决函数调用中的错误。建议从类路径、依赖关系和方法签名等方面入手,进行全面排查和调试。 ... [详细]
  • 深入解析Spring Boot启动过程中Netty异步架构的工作原理与应用
    深入解析Spring Boot启动过程中Netty异步架构的工作原理与应用 ... [详细]
  • JVM钩子函数的应用场景详解
    本文详细介绍了JVM钩子函数的多种应用场景,包括正常关闭、异常关闭和强制关闭。通过具体示例和代码演示,帮助读者更好地理解和应用这一机制。适合对Java编程和JVM有一定基础的开发者阅读。 ... [详细]
  • Tornado框架中模块与静态文件的应用
    本文详细介绍了Tornado框架中模块和静态文件的使用方法。首先明确模块与模板的区别,然后通过具体的代码示例展示如何在HTML文档中使用模块,并配置模块的路由。最后,提供了模块类中参数获取的示例。 ... [详细]
  • 尽管我们尽最大努力,任何软件开发过程中都难免会出现缺陷。为了更有效地提升对支持部门的协助与支撑,本文探讨了多种策略和最佳实践,旨在通过改进沟通、增强培训和支持流程来减少这些缺陷的影响,并提高整体服务质量和客户满意度。 ... [详细]
  • Netty框架中运用Protobuf实现高效通信协议
    在Netty框架中,通过引入Protobuf来实现高效的通信协议。为了使用Protobuf,需要先准备好环境,包括下载并安装Protobuf的代码生成器`protoc`以及相应的源码包。具体资源可从官方下载页面获取,确保版本兼容性以充分发挥其性能优势。此外,配置好开发环境后,可以通过定义`.proto`文件来自动生成Java类,从而简化数据序列化和反序列化的操作,提高通信效率。 ... [详细]
  • 本文详细介绍了在Windows XP系统中安装和配置Unix打印服务的方法,以支持远程行式打印机(LPR)功能。对于同时使用Windows 2000 Server打印服务器和Unix打印服务器的网络环境,该指南提供了实用的步骤和配置建议,确保不同平台之间的兼容性和高效打印。 ... [详细]
author-avatar
AIHFKYH_986
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有