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. 面包板入门电子制作(class1)视频 全套30集高清

    面包板入门电子制作(class1)套件(30集高清) 本套件以电子制作中最基础的元器件在面包板上搭建电路,用启发性的视频教学方式,使学习者熟悉电子电路基础.发挥想像力.在创新设计和制作中学会独立设计和 ...

  2. [2013 eoe移动开发者大会]靳岩:从码农到极客的升级之路

    (国内知名Android开发论坛 eoe开发者社区推荐:http://www.eoeandroid.com/) 前天,2013 eoe 移动开发者大会在国家会议中心召开,eoe 开发者社区创始人靳岩在 ...

  3. EventBus 3.0使用

    在没用eventBus之前一直用Android广播方式通知消息更新UI 广播写法 首先发送广播通知 Intent intent = new Intent(); intent.setAction(&qu ...

  4. mahout分类学习和遇到的问题总结

    这段时间学习Mahout有喜有悲.在这里首先感谢樊哲老师的指导.以下列出关于这次Mahout分类的学习和遇到的问题,还请大家多多提出建议:(全部文件操作都使用是在hdfs上边进行的). (本人用的环境 ...

  5. 匿名对象和object的转换

    有时候经常用到需要把一个匿名对象存入session或List<object>或其他容器中,可是取出来的时候变成object了,不太方便使用. 下面是一种转换方式:     class Pr ...

  6. 在 Excel 中使用公式拆分字符串日期

    如图所示,分别使用 LEFT.MIDB.RIGHT 来拆分再拼接字符串即可: =LEFT(A1,4)&"-"&MIDB(A1,5,2)&"-&qu ...

  7. 一个winform带你玩转rabbitMQ

    源码已放出 https://github.com/dubing/MaoyaRabbit 本章分3部分 一.安装部署初探 二.进阶 三.api相关 安装 部署 初探 先上图 一. 安装部署 下载 rab ...

  8. 日常工作中的点滴总结from 2014-03

    一 关于 写方案: 写某个产品的方案基本应包括以下几点: 1产品目前现状(国内外)  2产品意义.作用 3产品架构 4产品优势 5产品功能讲解 二 关于 处理下属工作方向不正确的事务 首先 先肯定 下 ...

  9. android 开发环境

    http://blog.csdn.net/shulianghan/article/details/38023959

  10. 使用word和pdf进行仿书编辑的经验

    一.问题的提出:    一本书扫描好,要将书中的图片转换为文字版的word文档.二.问题的分析:    1.文字的提取    2.文字的编排三.问题的解决    1.如果用的是Adobe Acroba ...