简介

semaphore中文意思既是信号量,它的主要功能就是用来控制某个资源同时被访问的线程数。

为了控制某块资源的并发访问量时,可以使用Semaphore对象中的acquire()方法获取访问令牌,如果Semaphore对象访问令牌已发完,那么当前获取令牌的线程将会进入阻塞,带其他线程进行release()释放令牌时,当前线程才有机会获得令牌从而拥有访问权限。

简述实现原理

Semaphore实际上是一种共享锁,因为它允许多个线程并发获取共享的资源。在Semaphore对象创建时必须设置可用令牌的初始数量permits,用于控制并发时同时获取资源权限的线程数量。在Semaphore类中继承了同步队列AbstractQueuedSynchronizer,在此类中有个属性state用于标记当前并发的队列数,也就是获取令牌的线程数,那么在进行acquire()的时候,就会尝试获取共享锁,获取锁成功后state值将加1,如果state值已经达到permits时就表示令牌已派发完,当前线程将进入阻塞状态,待其他线程进行release()state值将减1,此时就会从队列中获取头部线程进行唤醒让其获得令牌进行资源访问。

如下图,仔细查看源码就会发现Semaphore实际上就是重写了AbstractQueuedSynchronizer中的tryAcquireShared()tryReleaseShared()方法来实现的。

方法介绍

  • Semaphore重载了两个构造函数,其一是Semaphore(int permits)直接指定令牌数,默认为非公平锁;其二是Semaphore(int permits,boolean fair),fair参数即表示线程抢占令牌的公平性,true为公平锁,否则为非公平锁。
  • acquire()无参表示默认获取一个令牌,acquire(int permits)表示获取指定permits数量的令牌数,如果令牌不够,则当前线程进入阻塞状态。

tryAcquire()无参表示尝试获取一个令牌,该方法是非阻塞的,所以如果令牌数不够获取失败返回false,否则就返回true;同时也重载了方法tryAcquire(int permits)指定获取令牌数,tryAcquire(int permits, long timeout, TimeUnit unit)在有效时间内尝试获取指定数量的令牌数,如果超时仍未获取到令牌则返回false,否则返回true。 release同上支持无参与带参指定释放令牌数的方法。 drainPermits()获取剩下的可用令牌。 hasQueuedThread()用于判断当前Semaphare实例中是否存在等待获取令牌的线程。

案例分析

分析一下下面这段简短的代码,首先是创建一个信号量为5的Semaphore对象,然后在创建一个线程池(这里为了演示方便,实际开发中不建议使用此线程池创建),利用for循环并发运行100个线程,当线程运行时优先获取一个令牌,在线程中的业务代码里我们做了1秒的休眠,为了展示等待获取令牌的效果,在延迟1秒执行完业务代码时进行令牌释放,后续的线程才能逐个被唤醒获取令牌访问共享资源。

@Slf4j
public class SemaphoreDemo {

    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(5);
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 100; i  ) {
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    log.info("成功获取令牌");
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    log.info("释放令牌");
                    semaphore.release();
                }
            });
        }
        executorService.shutdown();
    }
}
  • 运行结果

由下图运行结果可见,100个线程并发并不是一次性都执行完的,而是要等待前面的线程释放令牌后等待的线程才可以获取令牌进行业务代码的运行。

适用场景

Semaphore主要是运用在多线程环境中对某一些共享资源的访问量限制,防止多个线程并发访问同一资源,可能会导致大多数线程获取资源时都需要进行加锁,那如果是获取数据库中的数据,那么就可以缓解数据库的压力。

另一种情况是用于多线程运行的一个流量限制,一般情况下我们可能会通过线程池做一步线程数的控制,但是某些业务为了减轻CPU的负担,还是会做一些同时运行线程数的限制。

到此这篇关于Java中的Semaphore如何使用的文章就介绍到这了,更多相关Semaphore使用内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

Java中的Semaphore如何使用的更多相关文章

  1. ios – 何时使用Semaphore而不是Dispatch Group?

    我会假设我知道如何使用DispatchGroup,为了解问题,我尝试过:结果–预期–是:为了使用信号量,我实现了:并在viewDidLoad方法中调用它.结果是:从概念上讲,dispachGroup和Semaphore都有同样的目的.老实说,我不熟悉:什么时候使用信号量,尤其是在与dispachGroup合作时–可能–处理问题.我错过了什么部分?

  2. Swift - 多线程实现方式3 - Grand Central DispatchGCD

    dispatchqueue可以是并发的或串行的。dispatch_suspend后,追加到DispatchQueue中尚未执行的任务在此之后停止执行。6//创建并行队列conQueue:dispatch_queue_t=dispatch_queue_create//暂停一个队列dispatch_suspend//继续队列dispatch_resume6,dispatch_once一次执行保证dispatch_once中的代码块在应用程序里面只执行一次,无论是不是多线程。注意,我们不能(直接)取消我们已经提

  3. Swift - 使用NSURLSession同步获取数据(通过添加信号量)

    从iOS9起,苹果建议废除NSURLConnection,使用NSURLSession代替NSURLConnection。如果想要NSURLSession也能够同步请求,即数据获取后才继续执行下面的代码,使用信号、信号量就可以实现。

  4. 在swift脚本中等待异步调用

    我正在编写一个swift脚本,在终端中运行,向后台线程调度几个操作.在完成所有调度之后,没有任何额外的努力,代码到达文件的末尾并退出,也杀死我的后台操作.在我的后台操作完成之前,保持swift脚本活着的最佳方法是什么?

  5. 如何在PHP环境中使用ProtoBuf数据格式

    这篇文章主要介绍了如何在PHP环境中使用ProtoBuf数据格式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

  6. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  7. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  8. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  9. 使用sockets:从新闻组中获取文章(三)

    >我们从服务器的这个新闻组中读取了最后的十篇文章,。也可以通过使用HEAD命令读取文章的头信息,或者使用BODY命令读取文章内容。>关于fclose()的更多信息,请参考http://www.php.net/manual/function.fclose.php结论在上文中,我们看到了怎样打开、使用然后关闭一个socket:连接到一个NNTP服务器,取回一些文章。使用POST命令发表文章也复杂不到哪儿去。下一步就是编写一个基于WEB的新闻组客户端了。这样,你有了一个基于web的搜索新闻组的程序了。

  10. Java实现世界上最快的排序算法Timsort的示例代码

    Timsort 是一个混合、稳定的排序算法,简单来说就是归并排序和二分插入排序算法的混合体,号称世界上最好的排序算法。本文将详解Timsort算法是定义与实现,需要的可以参考一下

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部