当我们创建一个可扩展大小的线程池,并且需要在线程池内同时让有限数目的线程并发运行时,就需要用到Semaphore(信号灯机制),Semaphore 通常用于限制可以访问某些资源(物理或逻辑的)的线程数目,它是一个计数信号量,从概念上讲,信号量维护了一个许可集合,如有必要,在许可可用前会阻塞每一个acquire(),然后再获取该许可,每个release() 添加一个许可,从而可能释放一个正在阻塞的获取者。
在线程池内创建线程并运行时,每个线程必须从信号量获取许可,从而保证可以使用该项。该线程结束后,线程返回到池中并将许可返回到该信号量,从而允许其他线程获取该项。注意,调用acquire() 时无法保持同步锁定,因为这会阻止线程返回到池中。信号量封装所需的同步,以限制对池的访问,这同维持该池本身一致性所需的同步是分开的。下面通过一个例子加以说明:
- public class SemaphoreTest {
- public static void main(String[] args) {
- ExecutorService service = Executors.newCachedThreadPool();
- final Semaphore sp = new Semaphore(3);
- for(int i=0;i<5;i++){
- Runnable runnable = new Runnable(){
- public void run(){
- try {
- sp.acquire();
- } catch (InterruptedException e1) {
- e1.printStackTrace();
- }
- System.out.println("线程" + Thread.currentThread().getName() +
- "进入,当前已有" + (3-sp.availablePermits()) + "个并发");
- try {
- Thread.sleep((long)(Math.random()*10000));
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("线程" + Thread.currentThread().getName() +
- "即将离开");
- sp.release();
- //下面代码有时候执行不准确,因为其没有和上面的代码合成原子单元
- System.out.println("线程" + Thread.currentThread().getName() +
- "已离开,当前已有" + (3-sp.availablePermits()) + "个并发");
- }
- };
- service.execute(runnable);
- }
- }
- }
该例子定义了一个newCachedThreadPool,在该Pool中利用for循环同时创建5个线程,现在通过Semaphore,创建一个只允许在线程池中有3个线程并发运行,sp.acquire()表示某个线程获得了一个信号灯,开始运行,在运行结束时,通过sp.release()还回这个信号灯,以便剩下的线程获得信号灯运行,sp.availablePermits()指的是当前信号灯库中有多少个可以被使用,由于例子中定义有3个信号灯,所以3-sp.availablePermits()就代表了当前有多少个线程在并发运行,上例运行结果如下:
- 线程pool-1-thread-1进入,当前已有2个并发
- 线程pool-1-thread-2进入,当前已有2个并发
- 线程pool-1-thread-3进入,当前已有3个并发
- 线程pool-1-thread-1即将离开
- 线程pool-1-thread-1已离开,当前已有2个并发
- 线程pool-1-thread-4进入,当前已有3个并发
- 线程pool-1-thread-3即将离开
- 线程pool-1-thread-3已离开,当前已有2个并发
- 线程pool-1-thread-5进入,当前已有3个并发
- 线程pool-1-thread-2即将离开
- 线程pool-1-thread-2已离开,当前已有2个并发
- 线程pool-1-thread-4即将离开
- 线程pool-1-thread-4已离开,当前已有1个并发
- 线程pool-1-thread-5即将离开
- 线程pool-1-thread-5已离开,当前已有0个并发
Semaphore作为互斥锁使用:
当信号量初始化为 1,使得它在使用时最多只有一个可用的许可,从而可用作一个相互排斥的锁。这通常也称为二进制信号量,因为它只能有两种状态:一个可用的许可,或零个可用的许可。按此方式使用时,与传统互斥锁最大不同就是在释放的时候并不是必须要拥有锁的对象释放,也可以由其他的对象释放,因为信号量没有所有权的概念。在某些专门的上下文(如死锁恢复)中这会很有用。
用信号量的方式(极端情况允许线程数1)实现的互斥锁,没有谁占有谁释放,这种通过限制线程数量的锁是线程执行完之后就释放,其他等待线程进入
Semaphore的构造方法有两种:
第一种:
- Semaphore(int permits) //用给定的许可数和非公平的公平设置创建一个 Semaphore。
第一种构造方法创建的信号灯,现在在获取的时候是随机的,没有一定的顺序,例如上例中,在前三个线程中的一个运行完毕以后,释放一个信号灯,剩下的两个线程就会随机的一个线程得到这个信号灯而运行。
第二种:
- Semaphore(int permits, boolean fair) //用给定的许可数和给定的公平设置创建一个 Semaphore
第二种构造方法可选地接受一个公平 参数。当设置为 false 时,此类不对线程获取许可的顺序做任何保证。特别地,闯入 是允许的,也就是说可以在已经等待的线程前为调用acquire() 的线程分配一个许可,从逻辑上说,就是新线程将自己置于等待线程队列的头部。当公平设置为 true 时,信号量保证对于任何调用acquire() 方法的线程而言,都按照处理它们调用这些方法的顺序(即先进先出;FIFO)来选择线程、获得许可。注意,FIFO 排序必然应用到这些方法内的指定内部执行点。所以,可能某个线程先于另一个线程调用了acquire(),但是却在该线程之后到达排序点,并且从方法返回时也类似。还要注意,非同步的tryAcquire() 方法不使用公平设置,而是使用任意可用的许可。
通常,应该将用于控制资源访问的信号量初始化为公平的,以确保所有线程都可访问资源。为其他的种类的同步控制使用信号量时,非公平排序的吞吐量优势通常要比公平考虑更为重要。
相关推荐
Semaphore-信号灯机制。 synchronized在静态方法和普通方法的区别。 怎么实现所有线程在等待某个事件的发生才会去执行。 CAS。 Hashtable是怎么加锁的。 HashMap的并发问题。 ConcurrenHashMap 介绍。 AQS。 如何...
Semaphore-信号灯机制。 synchronized在静态方法和普通方法的区别。 怎么实现所有线程在等待某个事件的发生才会去执行。 CAS。 Hashtable是怎么加锁的。 HashMap的并发问题。 ConcurrenHashMap 介绍。 AQS。 如何...
在Linux系统编程中,信号灯(Semaphore)是一种用于进程间通信的重要同步机制,它允许多个进程协调对共享资源的访问。本示例着重讲解如何使用C语言实现信号灯,包括信号灯的初始化、信号灯回滚、信号等待和信号释放...
本文将详细讲解Linux C编程中的一种进程间通信方式——信号灯(Semaphore),并结合提供的程序源码进行解析。 信号灯是同步原语之一,它允许进程在执行过程中对某些资源进行互斥访问或同步操作。信号灯由两个基本...
信号灯模型是并发编程中的一种常见机制,常用于线程间的同步与通信。在系统级芯片(System-on-a-Chip, SOC)上实现信号灯模型,可以有效地协调多个任务或线程,确保资源的安全访问和避免竞态条件。在本案例中,我们...
**信号灯法** 是一种同步机制,它维护一个计数器,可以用来限制对特定资源的访问。在生产者与消费者模型中,信号灯常被用来管理一个缓冲区,生产者可以将产品放入缓冲区,而消费者可以从缓冲区取出产品。当缓冲区满...
接下来,我们来看“信号灯”(Semaphore)。信号灯是另一种同步机制,它允许特定数量的线程访问一个共享资源。当信号灯计数值大于0时,线程可以获取一个许可证并进入临界区;当计数值为0时,其他试图获取许可证的...
在多线程环境中,信号灯(Semaphore)是一种同步机制,用于控制多个线程对共享资源的访问,避免竞态条件的发生。本文将深入探讨在Linux Red Hat 7.0环境下如何使用信号灯,并以提供的`test.c`源代码为例进行解析。 ...
信号灯分为两种类型:二进制信号灯(Binary Semaphore)和计数信号灯(Counting Semaphore)。 1. **二进制信号灯**:仅能取值0或1,类似于互斥锁,用于保护临界区,防止多个进程同时进入。 2. **计数信号灯**:...
任务间的通信和同步主要通过信号量(Semaphore)、消息队列(Message Queue)和事件标志组(Event Flag)等机制。在这个交通灯项目中,可能创建了多个任务,如控制红灯、绿灯和黄灯的任务,通过这些机制协调工作。 ...
操作系统中的信号量机制是一种重要的进程同步工具,源于交通信号灯的概念,用于协调系统内部进程的同步及互斥问题。信号量是一个系统全局变量,其值只能通过P(等待)和V(唤醒)这两个原子操作来改变。 信号量的...
实验的目的是让学生了解和掌握Linux环境下的线程创建、管理和信号灯机制。主要内容包括: - 创建和管理线程:学习如何在C语言中使用pthread库来创建和控制线程。 - 信号灯(Semaphore):理解信号灯作为进程间通信...
2. **信号灯机制**:在Linux中,可以使用`semaphore`结构体和`semop`函数操作信号灯。`semop`函数用于对信号灯进行原子操作,如P操作(减操作,尝试获取资源)和V操作(加操作,释放资源)。在抽烟者问题中,供应者...
为了确保信号灯状态的正确切换,开发者可能会使用`synchronized`关键字、`wait()`和`notify()`方法,或者使用`java.util.concurrent`包中的高级并发工具,如`Semaphore`或`CyclicBarrier`。这些工具可以帮助避免竞态...
为了解决这个问题,我们可以利用Java编程语言中的交通信号灯机制来实现一种解决方案,这就是CenaFilosofos项目的核心所在。 首先,我们来理解交通信号灯机制。在现实生活中,交通信号灯通过红、黄、绿三种颜色的...
信号灯(Semaphore)是一种同步机制,用于控制多个线程访问共享资源的数量。信号灯可以设置一个初始值,表示可以访问资源的线程数量。当一个线程访问资源时,需要执行 P 操作(wait 操作),将信号灯的值减 1;当...
信号灯是多线程中的一种同步机制,用于协调线程之间的执行顺序。 知识点2:信号灯的使用 信号灯是一种同步机制,用于协调线程之间的执行顺序。信号灯可以分为两种:互斥信号灯和信号量。互斥信号灯用于实现上锁...
在IT领域,尤其是在并发编程和分布式系统中,信号灯(信号量)是一种重要的同步机制。本文将深入探讨标题“信号灯:使用ETS的快速信号灯”所涉及的知识点,主要聚焦于Erlang和Elixir语言中的信号量实现以及ETS...
在Linux操作系统中,实现并发程序通常涉及到进程同步和通信,其中信号灯(Semaphore)是一种重要的同步机制。在给定的程序示例中,通过C语言编写了四个源程序:main.c、get.c、copy.c和put.c,这些程序共同协作完成...
UCOSII是一个实时操作系统,它提供了多种机制来管理和同步任务,包括信号量、互斥信号量和事件标志组。这些机制对于多任务环境中的资源管理至关重要。 1. 互斥信号量(Mutex Semaphore): 互斥信号量的主要作用是...