Semaphore-信号灯机制

我们创建一个可扩展大小的线程池,并且需要在线程池内同时让有限数目的线程并发运行时,就需要用到Semaphore(信号灯机制),Semaphore 通常用于限制可以访问某些资源(物理或逻辑的)的线程数目,它是一个计数信号量,从概念上讲,信号量维护了一个许可集合,如有必要,在许可可用前会阻塞每一个acquire(),然后再获取该许可,每个release() 添加一个许可,从而可能释放一个正在阻塞的获取者。

在线程池内创建线程并运行时,每个线程必须从信号量获取许可,从而保证可以使用该项。该线程结束后,线程返回到池中并将许可返回到该信号量,从而允许其他线程获取该项。注意,调用acquire() 时无法保持同步锁定,因为这会阻止线程返回到池中。信号量封装所需的同步,以限制对池的访问,这同维持该池本身一致性所需的同步是分开的。下面通过一个例子加以说明:

 1 import java.util.concurrent.ExecutorService;
 2 import java.util.concurrent.Executors;
 3 import java.util.concurrent.Semaphore;
 4
 5 //信号灯测试
 6 public class SemaphoreTest {
 7     public static void main(String[] args) {
 8         ExecutorService service = Executors.newCachedThreadPool();
 9         final Semaphore sp = new Semaphore(3);//开启3个信号灯
10         for (int i = 0; i < 10; i++) {
11             Runnable runnable = new Runnable() {
12                 public void run() {
13                     try {
14                         sp.acquire();
15                     } catch (InterruptedException e1) {
16                         e1.printStackTrace();
17                     }
18                     System.out.println("线程" + Thread.currentThread().getName() +
19                             "进入,当前已有" + (3 - sp.availablePermits()) + "个并发");
20                     try {
21                         Thread.sleep((long) (Math.random() * 1000));//休息的时间越短的话,就越容易打乱进入
22                     } catch (InterruptedException e) {
23                         e.printStackTrace();
24                     }
25                     System.out.println("线程" + Thread.currentThread().getName() +
26                             "即将离开");
27                     sp.release();
28                     //下面代码有时候执行不准确,因为其没有和上面的代码合成原子单元
29                     System.out.println("线程" + Thread.currentThread().getName() +
30                             "已离开,当前已有" + (3 - sp.availablePermits()) + "个并发");
31                 }
32             };
33             service.execute(runnable);
34         }
35     }
36
37 }

该例子定义了一个newCachedThreadPool,在该Pool中利用for循环同时创建5个线程,现在通过Semaphore,创建一个只允许在线程池中有3个线程并发运行,sp.acquire()表示某个线程获得了一个信号灯,开始运行,在运行结束时,通过sp.release()还回这个信号灯,以便剩下的线程获得信号灯运行,sp.availablePermits()指的是当前信号灯库中有多少个可以被使用,由于例子中定义有3个信号灯,所以3-sp.availablePermits()就代表了当前有多少个线程在并发运行.

Semaphore作为互斥锁使用:

当信号量初始化为 1,使得它在使用时最多只有一个可用的许可,从而可用作一个相互排斥的锁。这通常也称为二进制信号量,因为它只能有两种状态:一个可用的许可,或零个可用的许可。按此方式使用时,与传统互斥锁最大不同就是在释放的时候并不是必须要拥有锁的对象释放,也可以由其他的对象释放,因为信号量没有所有权的概念。在某些专门的上下文(如死锁恢复)中这会很有用。

Semaphore的构造方法有两种:

第一种:

Semaphore(int permits) //用给定的许可数和非公平的公平设置创建一个 Semaphore。  

第一种构造方法创建的信号灯,现在在获取的时候是随机的,没有一定的顺序,例如上例中,在前三个线程中的一个运行完毕以后,释放一个信号灯,剩下的两个线程就会随机的一个线程得到这个信号灯而运行。

第二种:

Semaphore(int permits, boolean fair) //用给定的许可数和给定的公平设置创建一个 Semaphore  

第二种构造方法可选地接受一个公平 参数。当设置为 false 时,此类不对线程获取许可的顺序做任何保证。特别地,闯入 是允许的,也就是说可以在已经等待的线程前为调用acquire() 的线程分配一个许可,从逻辑上说,就是新线程将自己置于等待线程队列的头部。当公平设置为 true 时,信号量保证对于任何调用acquire() 方法的线程而言,都按照处理它们调用这些方法的顺序(即先进先出;FIFO)来选择线程、获得许可。注意,FIFO 排序必然应用到这些方法内的指定内部执行点。所以,可能某个线程先于另一个线程调用了acquire(),但是却在该线程之后到达排序点,并且从方法返回时也类似。还要注意,非同步的tryAcquire() 方法不使用公平设置,而是使用任意可用的许可。
      通常,应该将用于控制资源访问的信号量初始化为公平的,以确保所有线程都可访问资源。为其他的种类的同步控制使用信号量时,非公平排序的吞吐量优势通常要比公平考虑更为重要。

原文地址:https://www.cnblogs.com/tinya/p/8452597.html

时间: 2024-11-29 11:00:59

Semaphore-信号灯机制的相关文章

java 5线程中 Semaphore信号灯,CyclicBarrier类,CountDownLatch计数器以及Exchanger类使用

先来讲解一下Semaphore信号灯的作用:  可以维护当前访问自身的线程个数,并提供了同步机制, 使用semaphore可以控制同时访问资源的线程个数 例如,实现一个文件允许的并发访问数. 请看下面的演示代码: 1 public class SemaphoreTest 2 { 3 public static void main(String[] args) 4 { 5 //创建一个带有缓存的线程池 6 ExecutorService service = Executors.newCachedT

Java多线程——Semaphore信号灯

Semaphore可以维护当前访问自身的线程个数,并提供了同步机制.使用Semaphore可以控制同时访问资源的线程个数(即允许n个任务同时访问这个资源),例如,实现一个文件允许的并发访问数. Semaphore实现的功能就类似厕所有5个坑,假如有十个人要上厕所,那么同时能有多少个人去上厕所呢?同时只能有5个人能够占用,当5个人中的任何一个人让开后,其中在等待的另外5个人中又有一个可以占用了. 另外等待的5个人中可以是随机获得优先机会,也可以是按照先来后到的顺序获得机会,这取决于构造Semaph

Linux多线程同步机制

http://blog.163.com/he_junwei/blog/static/19793764620141711130253/ http://blog.csdn.net/h_armony/article/details/6766505  一.互斥锁 尽管在Posix Thread中同样可以使用IPC的信号量机制来实现互斥锁mutex功能,但显然semphore的功能过于强大了,在Posix Thread中定义了另外一套专门用于线程同步的mutex函数. 1. 创建和销毁 有两种方法创建互斥

线程同步机制(二)-- 线程同步辅助类

我们在线程同步机制(一)--Synchronized和Lock简要介绍中学习了同步和临界区的概念,并且讨论了多个并发任务共享一个资源时的同步情况.访问共享资源的代码块叫临界区. 我们在线程同步机制(一)--Synchronized和Lock简要介绍中学习了一下内容: synchronized关键字 Lock接口及其实现类,如ReentrantLock,ReentrantReadWriteLock.ReadLock和ReentrantReadWriteLock.WriteLock 本章我们将学习如

python lock, semaphore, event实现线程同步

lock 机制不管你是java, C#, 还是python都是常用的线程同步机制, 相比较C# 的锁机制, python的加锁显得比较简单, 直接调用threading 标准库的lock 就可以了. python 的 lock类有两个函数, 分别是acquire 函数以及 release 函数, 前者起到锁定的作用, 将状态设置为锁定状态, 后者则是解锁, 将状态设置为未锁定状态. 我们看看代码: # python 多线程同步 lock import threading from time im

线程同步(互斥锁与信号量的作用与区别)

“信号量用在多线程多任务同步的,一个线程完成了某一个动作就通过信号量告诉别的线程,别的线程再进行某些动作(大家都在semtake的时候,就阻塞在 哪里).而互斥锁是用在多线程多任务互斥的,一个线程占用了某一个资源,那么别的线程就无法访问,直到这个线程unlock,其他的线程才开始可以利用这 个资源.比如对全局变量的访问,有时要加锁,操作完了,在解锁.有的时候锁和信号量会同时使用的” 也就是说,信号量不一定是锁定某一个资源,而是流程上的概念,比如:有A,B两个线程,B线程要等A线程完成某一任务以后

Linux多线程

1. Linux多线程概述 1.1. 概述 进程是系统中程序执行和资源分配的基本单位.每个进程有自己的数据段.代码段和堆栈段.这就造成进程在进行切换等操作时都需要有比较负责的上下文切换等动作.为了进一步减少处理器的空转时间支持多处理器和减少上下文切换开销,也就出现了线程. 线程通常叫做轻量级进程.线程是在共享内存空间中并发执行的多道执行路径,是一个更加接近于执行体的概念,拥有独立的执行序列,是进程的基本调度单元,每个进程至少都有一个main线程.它与同进程中的其他线程共享进程空间{堆 代码 数据

words2

餐具:coffee pot 咖啡壶coffee cup 咖啡杯paper towel 纸巾napkin 餐巾table cloth 桌布tea -pot 茶壶tea set 茶具tea tray 茶盘caddy 茶罐dish 碟plate 盘saucer 小碟子rice bowl 饭碗chopsticks 筷子soup spoon 汤匙knife 餐刀cup 杯子glass 玻璃杯mug 马克杯picnic lunch 便当fruit plate 水果盘toothpick 牙签中餐:bear's

python进程-进阶

进程同步(multiprocessing.Lock(锁机制).multiprocessing.Semaphore(信号量机制).multiprocessing.Event(事件机制)) 在计算机中,有一些硬件和软件,例如处理器.打印机等,都属于竞争类资源,当有需求时,很多进程都要争抢这些资源,而对于这类资源,就属于临界资源.当多进程共同处理某一个数据时,这个数据也就属于一个临界资源.操作系统对计算机内各种资源都使其在竞争中有序化,但是对于数据来说,尤其是用户动态产生的数据,当处理时就变成了临界资