Semaphore也是一个线程同步的辅助类,可以维护当前访问自身的线程个数,并提供了同步机制。
  使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数。

一个计数信号量。从概念上讲,信号量维护了一个许可集。
如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。每个 release() 添加一个许可,从而可能释放一个正在阻塞的获取者。
但是,不使用实际的许可对象,Semaphore 只对可用许可的号码进行计数,并采取相应的行动。拿到信号量的线程可以进入代码,否则就等待。

主要方法摘要:
   void acquire():从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断
   void release():释放一个许可,将其返回给信号量
   int availablePermits():返回此信号量中当前可用的许可数
   boolean hasQueuedThreads():查询是否有线程正在等待获取
1.简单例子

 维护当前访问自身的线程个数
@Test
public void semaphore1Test() throws InterruptedException {
// 线程池
ExecutorService exec = Executors.newCachedThreadPool();
// 只能5个线程同时访问
final Semaphore semp = new Semaphore(5);
// 模拟20个客户端访问
for (int index = 0; index < 20; index++) {
final int NO = index;
Runnable run = new Runnable() {
public void run() {
try {
// 请求获得许可,如果有可获得的许可则继续往下执行,许可数减1。否则进入阻塞状态
semp.acquire();
System.out.println("Accessing: " + NO);
Thread.sleep((long) (Math.random() * 10000));
// 访问完后,释放许可,许可数加1,如果屏蔽下面的语句,则在控制台只能打印5条记录,之后线程一直阻塞
semp.release();
System.out.println("线程" + Thread.currentThread().getName() + "已离开,当前已有" + (3-semp.availablePermits()) + "个并发");
} catch (InterruptedException e) {
}
}
};
exec.execute(run);
}
// 退出线程池
exec.shutdown();
}

2.当锁

当信号量的数量上限是1时,Semaphore可以被当做锁来使用。通过acquire和release方法来保护关键区域。

    @Test
public void semaphore2Test() throws InterruptedException {
final Business business = new Business();
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 0; i < 3; i++) {
executor.execute(new Runnable() {
public void run() {
business.service();
}
}
);
}
executor.shutdown();
}
class Business {
private int count;
Lock lock = new ReentrantLock();
Semaphore sp = new Semaphore(1);
public void service() {
// lock.lock();
try {
sp.acquire(); // 当前线程使用count变量的时候将其锁住,不允许其他线程访问
} catch (InterruptedException e1) {
e1.printStackTrace();
}
try {
count++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(count);
} catch (RuntimeException e) {
e.printStackTrace();
} finally {
// lock.unlock();
sp.release(); // 释放锁
}
}
}

3.生产者消费者模型,生成阻塞队列

a.消息通道

class SemaphoreQueue {
private List<Integer> valueList;
private Semaphore putActionNum;// 可以进行put操作的许可数量
private Semaphore getActionNum;// 可以进行take操作的许可数量
private Semaphore mutex; //相当于锁 控制非线程安全的valueList的操作 public SemaphoreQueue(int capacity) {
putActionNum = new Semaphore(capacity);// 维护队列大小
getActionNum = new Semaphore(0); // 初始化时,队列为空,put操作许可数量为0
mutex = new Semaphore(1); // 用于保护非线程安全的valueList操作,用于并发生产时控制
valueList = new ArrayList<Integer>(capacity);
} public void put(Integer message) {
try {
putActionNum.acquire();// put操作许可减1
mutex.acquire();
valueList.add(message);
mutex.release();
getActionNum.release();// get操作许可加1
} catch (InterruptedException e) {
e.printStackTrace();
}
} public Integer take() {
Integer message = null;
try {
getActionNum.acquire();// get操作许可减1
mutex.acquire();
if (valueList.size() > 0) {
message = valueList.get(0);
valueList.remove(0);
} else {
return null;
}
mutex.release();
putActionNum.release();// put操作许可加1
} catch (InterruptedException e) {
e.printStackTrace();
}
return message;
}
}

b. 生产者和消费者

class Productor extends Thread {
SemaphoreQueue queue;
public Productor(SemaphoreQueue queue) {
this.queue = queue;
} public void run() {
int i = 0;
try {
while (true) {
i++;
Integer message = new Integer(i);
queue.put(message);
if (i % 20 == 0) {
System.out.println("======== " + this.getName() + " 累计生产了 " + i + " 条消息 =======");
Thread.currentThread().sleep(1000);
} }
} catch (Exception e) {
e.printStackTrace();
}
}
} class Cousumertor extends Thread {
SemaphoreQueue queue;
public Cousumertor(SemaphoreQueue queue) {
this.queue = queue;
}
public void run() {
try {
while (true) {
Integer message = queue.take();
if (message != null) {
System.out.println("======== " + this.getName() + " 消费消息:" + message + " =======");
}
Thread.currentThread().sleep(100);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

c.测试

    public static void main(String[] args) {
SemaphoreQueue queue = new SemaphoreQueue(20);
// 开始生产
Productor productor = new Productor(queue);
productor.setName("生产者");
productor.start();
// 开始消费
Cousumertor c1 = new Cousumertor(queue);
c1.setName("消费者-c1");
Cousumertor c2 = new Cousumertor(queue);
c2.setName("消费者-c2");
c1.start();
c2.start();
}

4. Semaphore vs. CountDownLatch
   相同点 :
      两者都是用于线程同步的工具类,都通过定义了一个继承AbstractQueuedSynchronizer的内部类Sync来实现具体的功能.
   不同点 :
  a. Semaphore提供了公平和非公平两种策略, 而CountDownLatch则不具备.
  b. CountDownLatch: 一个或者是一部分线程,等待另外一部线程都完成操作。
     Semaphorr: 维护一个许可集.通常用于限制可以访问某些资源(物理或逻辑的)的线程数目.

  c. CountDownLatch中计数是不能被重置的。CountDownLatch适用于一次同步。当使用CountDownLatch时,任何线程允许多次调用countDown(). 那些调用了await()方法的线程将被阻塞,直到那些没有被阻塞线程调用countDown()使计数到达0为止 。
  Semaphore允许线程获取许可, 未获得许可的线程需要等待.这样防止了在同一时间有太多的线程执行.Semaphore的值被获取到后是可以释放的,并不像CountDownLatch那样一直减到0。

  d. 使用CountDownLatch时,它关注的一个线程或者多个线程需要在其它在一组线程完成操作之后,在去做一些事情。比如:服务的启动等。使用Semaphore时,它关注的是某一个资源最多同时能被几个线程访问.

thread_Semaphore信号量的更多相关文章

  1. ucos实时操作系统学习笔记——任务间通信(信号量)

    ucos实时操作系统的任务间通信有好多种,本人主要学习了sem, mutex, queue, messagebox这四种.系统内核代码中,这几种任务间通信机制的实现机制相似,接下来记录一下本人对核心代 ...

  2. Linux 信号量详解一

    信号量主要用于进程间(不是线程)的互斥,通过sem_p()函数加锁使用资源,sem_v函数解锁释放资源,在加锁期间,CPU从硬件级别关闭中断,防止pv操作被打断. semget函数 int semge ...

  3. PHP进程通信基础——信号量+共享内存通信

    PHP进程通信基础--信号量+共享内存通信 由于进程之间谁先执行并不确定,这取决于内核的进程调度算法,其中比较复杂.由此有可能多进程在相同的时间内同时访问共享内存,从而造成不可预料的错误.信号量这个名 ...

  4. C#多线程--信号量(Semaphore)

    百度百科:Semaphore,是负责协调各个线程, 以保证它们能够正确.合理的使用公共资源.也是操作系统中用于控制进程同步互斥的量. Semaphore常用的方法有两个WaitOne()和Releas ...

  5. Linux学习笔记(15)-信号量

    在多线程或者多进程编程中,有一个非常需要关注的东西,那就是同步以及互斥问题. 同步是指多个进程之间的协作,而互斥是指多个进程之间,为了争夺有限的资源,而进行的竞争. 理论很高端,但经过自己几天的学习, ...

  6. 多线程之信号量(By C++)

    信号量在多线程中,主要是用于线程的同步或者限制线程运行的数量. 所谓同步,当流程1运行在线程1中,流程2运行在线程2中,流程2必须在流程1结束之后才能开始执行.你会怎么做,所有就需要给出一个流程1结束 ...

  7. 信号量sem

    一.什么是信号量 为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,它可以通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域.临界区域是指执行数据更 ...

  8. 【.NET深呼吸】线程信号量(Semaphore)

    Semaphore类可以控制某个资源允许访问的线程数,Semaphore有命名式的,也有不命名的:如果不考虑跨进程工作,一般在代码中使用不命名方式即可. 信号量有点类似于等待句柄,某个线程如果调用了W ...

  9. java多线程--信号量Semaphore的使用

    Semaphore可以控制某个共享资源可被同时访问的次数,即可以维护当前访问某一共享资源的线程个数,并提供了同步机制.例如控制某一个文件允许的并发访问的数量. 例如网吧里有100台机器,那么最多只能提 ...

随机推荐

  1. Ques核心思想——CSS Namespace

    Facebook’s challenges are applicable to any very complex websites with many developers. Or any situa ...

  2. linux 文件夹说明,用户添加删除,不熟悉的命令

    一.Linux 根目录下的文件夹说明 usr 程序默认安装路径,相当于windows的 program 附显示当前所处位置:pwd 二.用户 用户添加:useradd 用户名 passwd 用户名 u ...

  3. Navi.Soft30.产品.Net对象查看器.操作手册

    1系统简介 1.1功能简述 在软件开发过程中,我们会编写各种类以及创建类的属性,方法,事件等.特别是第三方控件或组件,刚拿到手时,若没有完善的开发文档,很难下手.这时,若是可以查看这些DLL的成员对象 ...

  4. atom 安装插件出现 EIO 错误

    今天给 atom 安装一个插件autocomplete-python的时候出现错误 npm ERR! Windows_NT 6.1.7600 npm ERR! argv "C:\\Progr ...

  5. 实现 Dispose 方法

    实现 Dispose 方法 MSDN 类型的 Dispose 方法应释放它拥有的所有资源.它还应该通过调用其父类型的 Dispose 方法释放其基类型拥有的所有资源.该父类型的 Dispose 方法应 ...

  6. 构建基于WinRT的WP8.1 App 02:数据绑定新特性

    基于WinRT的Windows Phone 8.1以及Windows 8.1中Xaml数据绑定增加了一些新特性. FallBackValue属性:FallBackValue在绑定的值属性值不存在时,可 ...

  7. SQL Server 数据库操作类

    /// <summary> /// SQLServerHelper的摘要说明. /// </summary> public class SQLServerHelper { pu ...

  8. GET到新技能,SharpCEF,C#和JS的互相调用

    winform程序内嵌谷歌浏览器,使用大名鼎鼎的“SharpCEF”.这里科普一下: CEF是什么 CEF是Chromium Embedded Framework的缩写,是个基于Google Chro ...

  9. Node.js 的module 系统

    相较于原生的JavaScript,不同的JavaScript文件之间很难共享变量.有鉴于此,Node.js在JavaScript的基础上进行了扩充,引入了require,exports,module三 ...

  10. 从function前面的!想到的

    最近没事喜欢看看,一些js库的源码,结果发现库前不是加一个!就是加+或者一个(),心中猜出个大概知道这个是让函数自动执行,可是这么多符号达到同一个目的,原理是什么呢,下面做一下剖析: 先从IIFE开始 ...