关于java:Java-多线程并发控制工具信号量-Semaphore实现原理及案例

5次阅读

共计 2278 个字符,预计需要花费 6 分钟才能阅读完成。

信号量(Semaphore)是 Java 多线程兵法中的一种 JDK 内置同步器,通过它能够实现多线程对公共资源的并发访问控制。一个线程在进入公共资源时须要先获取一个许可,如果获取不到许可则要期待其它线程开释许可,每个线程在来到公共资源时都会开释许可。其实能够将 Semaphore 看成一个计数器,当计数器的值小于许可最大值时,所有调用 acquire 办法的线程都能够失去一个许可从而往下执行。而调用 release 办法则能够让计数器的值减一。

信号量的次要利用场景是管制最多 N 个线程同时地拜访资源,其中计数器的最大值即是许可的最大值 N。以停车场为例,假如停车场一共有 8 个车位,其中 6 个车位已被停放,而后来了两辆汽车,此时因为刚好剩下两个车位所以这两辆车都能停放。接着又来了一辆车,当初曾经没有空位了所以只能期待其它车来到。此时刚好一辆红色汽车来到停车场,来开后黄车刚好能够停进去,如果又有一辆汽车进来则该车又得期待。如此往返。这个过程中停车场就是公共资源,车位数就是信号量最大许可数,车辆就好比线程。

四因素

信号量的四因素为:最大许可数 偏心模式 acquire 办法 以及release 办法。最大许可数和偏心模式在构建 Semaphore 对象时指定,别离示意公共资源最多能够多少个线程同时拜访以及获取许可时是否应用偏心模式。acquire 办法用于获取许可,如果因为有余许可的话则进入期待状态。release 办法用于开释许可。

非偏心模式的实现

Semaphore 类的实现是基于 AQS 同步器来实现的,不论是偏心模式还是非偏心模式都是基于 AQS 的共享模式,只是在获取许可的操作逻辑有差别。Semaphore 的默认模式为非偏心模式,咱们先看非偏心模式的实现。

Semaphore 类的几个次要办法如下所示,其中提供了两个构造函数,相干的两个参数为许可最大数和是否应用偏心模式,其中 FairSync 是偏心模式的同步器而 NonfairSync 则是非偏心模式的同步器。有两个 acquire 办法,无参时默认是一次获取 1 个许可,而如果传入整型参数则示意一次获取若干个许可。对应地,也有两个 release 办法,无参时示意开释 1 个许可,而整型参数则示意一次开释若干个许可。Semaphore 次要的几个办法如下

Semaphore 外部的 Syn 子类是偏心模式 FairSync 类和非偏心模式 NonfairSync 类的形象父类,许可最大数与 AQS 同步器的状态变量对应。因为模式是非偏心模式,所以这里提供了非偏心的许可获取办法 nonfairTryAcquireShared。非偏心模式其实就是在许可数量容许的状况下,让所有线程都进行自旋操作,而不论它们先来后到的程序,全副线程放到一起去竞争许可。其中 compareAndSetState 办法提供了 CAS 算法从而可能保障并发批改许可值,而残余许可数等于以后可用许可值减去以后耗费许可数,须要留神的是当残余许可数小于 0 时则返回正数从而导致线程会进入期待队列中。tryReleaseShared 办法则提供了开释许可的操作,不论是不是偏心模式都应用该办法即可,开释许可的逻辑是雷同的。通过自旋操作来将开释的许可数减少到以后残余许可数。

非偏心模式 NonfairSync 类的实现次要是 tryAcquireShared 办法,间接调用父类 Sync 的的 nonfairTryAcquireShared 办法即可。

偏心模式实现

偏心模式与非偏心模式的次要差别就在获取许可时的机制,非偏心模式间接通过自旋操作让所有线程竞争许可,从而导致了非偏心。而偏心模式则通过队列来实现偏心机制。它们的差别就在 tryAcquireShared 办法,咱们看偏心模式的 tryAcquireShared 办法。实际上不同的中央就在下图中加了方框的两行代码,它会查看是否曾经存在期待队列,如果曾经有期待队列则返回 -1,返回 - 1 则示意让 AQS 同步器将以后线程进入期待队列中,队列则意味着偏心。实际上,这也并非是严格的偏心,在后面讲到的 AQS 同步器的公平性章节有深刻讲过 AQS 的公平性,如果遗记了能够从新查阅加深了解。而且在为达到最大许可数的状况下,所有线程也并没有进入期待队列中,而是全副线程进行自旋获取许可。

案例 1

咱们先看一个简略的例子,首先实例化一个领有 5 个许可的信号量对象,而后一共有 10 个线程一起尝试获取 5 个许可,得到许可的线程将 value 进行累加 1,接着睡眠五秒,最初开释许可。

以上程序输入如下,其中有五个线程输入“counting number : xx”后其余线程则开始期待。大略期待 5 秒后取得许可的五个线程执行开释许可操作,而后其它线程能力取得许可并往下执行。

案例 2

例子二与例子一很类似,不同的中央在于每次获取许可时会耗费 2 个许可,同样开释时也开释 2 个许可。这里实例化一个领有 6 个许可的信号量对象,而后 10 个线程一起尝试获取许可。但这次最多只能同时 3 个线程得到许可,也就是三个线程得到许可后对 value 值进行累加 1,而后睡眠 5 秒后开释许可。接着另外三个线程又取得许可往下执行,直到 10 个线程都执行完。

总结

本文介绍了一个 JDK 内置的同步器——信号量(Semaphore),通过它可能管制最多若干个线程拜访公共资源。它能够看成是一个计数器,当计数器的值小于许可最大值时线程可能往下执行,反之线程则只能期待。咱们深入分析了 Semaphore 的实现原理,它基于 AQS 同步器进行实现且提供了偏心和非偏心两种模式,并且咱们对这两种模式的实现别离进行了剖析。通过本文咱们曾经可能很深刻清晰了解 Semaphore 的原理机制了。

正文完
 0