以下内容转自http://ifeve.com/semaphore/

Semaphore(信号量) 是一个线程同步结构,用于在线程间传递信号,以避免出现信号丢失(译者注:下文会具体介绍),或者像锁一样用于保护一个关键区域。自从5.0开始,jdk在java.util.concurrent包里提供了Semaphore的官方实现,因此大家不需要自己去实现Semaphore。但是还是很有必要去熟悉如何使用Semaphore及其背后的原理

本文的涉及的主题如下:

一、简单的Semaphore实现

下面是一个信号量的简单实现:

public class Semaphore {
private boolean signal = false; public synchronized void take() {
this.signal = true;
this.notify();
} public synchronized void release() throws InterruptedException{
while(!this.signal) wait();
this.signal = false;
} }

take方法发出一个被存放在Semaphore内部的信号,而release方法则等待一个信号,当其接收到信号后,标记位signal被清空,然后该方法终止。

使用这个semaphore可以避免错失某些信号通知。用take方法来代替notify,release方法来代替wait。如果某线程在调用release等待之前调用take方法,那么调用release方法的线程仍然知道take方法已经被某个线程调用过了,因为该Semaphore内部保存了take方法发出的信号。而wait和notify方法就没有这样的功能。

当用semaphore来产生信号时,take和release这两个方法名看起来有点奇怪。这两个名字来源于后面把semaphore当做锁的例子,后面会详细介绍这个例子,在该例子中,take和release这两个名字会变得很合理。

二、使用Semaphore来产生信号

下面的例子中,两个线程通过Semaphore发出的信号来通知对方

Semaphore semaphore = new Semaphore();

SendingThread sender = new SendingThread(semaphore);

ReceivingThread receiver = new ReceivingThread(semaphore);

receiver.start();
sender.start();
public class SendingThread {
Semaphore semaphore = null; public SendingThread(Semaphore semaphore){
this.semaphore = semaphore;
} public void run(){
while(true){
//do something, then signal
this.semaphore.take(); }
}
}
public class RecevingThread {
Semaphore semaphore = null; public ReceivingThread(Semaphore semaphore){
this.semaphore = semaphore;
} public void run(){
while(true){
this.semaphore.release();
//receive signal, then do something...
}
}
}

三、可计数的Semaphore

上面提到的Semaphore的简单实现并没有计算通过调用take方法所产生信号的数量。可以把它改造成具有计数功能的Semaphore。下面是一个可计数的Semaphore的简单实现。

public class CountingSemaphore {
private int signals = 0; public synchronized void take() {
this.signals++;
this.notify();
} public synchronized void release() throws InterruptedException{
while(this.signals == 0) wait();
this.signals--;
} }

四、有上限的Semaphore

上面的CountingSemaphore并没有限制信号的数量。下面的代码将CountingSemaphore改造成一个信号数量有上限的BoundedSemaphore。

public class BoundedSemaphore {
private int signals = 0;
private int bound = 0; public BoundedSemaphore(int upperBound){
this.bound = upperBound;
} public synchronized void take() throws InterruptedException{
while(this.signals == bound) wait();
this.signals++;
this.notify();
} public synchronized void release() throws InterruptedException{
while(this.signals == 0) wait();
this.signals--;
this.notify();
}
}

在BoundedSemaphore中,当已经产生的信号数量达到了上限,take方法将阻塞新的信号产生请求,直到某个线程调用release方法后,被阻塞于take方法的线程才能传递自己的信号。

五、把Semaphore当锁来使用

当信号量的数量上限是1时,Semaphore可以被当做锁来使用。通过take和release方法来保护关键区域。请看下面的例子:

BoundedSemaphore semaphore = new BoundedSemaphore(1);

...

semaphore.take();

try{
//critical section
} finally {
semaphore.release();
}

在前面的例子中,Semaphore被用来在多个线程之间传递信号,这种情况下,take和release分别被不同的线程调用。但是在锁这个例子中,take和release方法将被同一线程调用,因为只允许一个线程来获取信号(允许进入关键区域的信号),其它调用take方法获取信号的线程将被阻塞,知道第一个调用take方法的线程调用release方法来释放信号。对release方法的调用永远不会被阻塞,这是因为任何一个线程都是先调用take方法,然后再调用release。

通过有上限的Semaphore可以限制进入某代码块的线程数量。设想一下,在上面的例子中,如果BoundedSemaphore 上限设为5将会发生什么?意味着允许5个线程同时访问关键区域,但是你必须保证,这个5个线程不会互相冲突。否则你的应用程序将不能正常运行。

必须注意,release方法应当在finally块中被执行。这样可以保在关键区域的代码抛出异常的情况下,信号也一定会被释放。

24、Java并发性和多线程-信号量的更多相关文章

  1. Java并发性和多线程介绍

    java并发性和多线程介绍: 单个程序内运行多个线程,多任务并发运行 多线程优点: 高效运行,多组件并行.读->操作->写: 程序设计的简单性,遇到多问题,多开线程就好: 快速响应,异步式 ...

  2. java 并发性和多线程 -- 读感 (一 线程的基本概念部分)

    1.目录略览      线程的基本概念:介绍线程的优点,代价,并发编程的模型.如何创建运行java 线程.      线程间通讯的机制:竞态条件与临界区,线程安全和共享资源与不可变性.java内存模型 ...

  3. Java并发性和多线程

    Java并发性和多线程介绍   java并发性和多线程介绍: 单个程序内运行多个线程,多任务并发运行 多线程优点: 高效运行,多组件并行.读->操作->写: 程序设计的简单性,遇到多问题, ...

  4. Java 并发和多线程(一) Java并发性和多线程介绍[转]

    作者:Jakob Jenkov 译者:Simon-SZ  校对:方腾飞 http://tutorials.jenkov.com/java-concurrency/index.html 在过去单CPU时 ...

  5. Java高级教程:Java并发性和多线程

    Java并发性和多线程: (中文,属于人工翻译,高质量):http://ifeve.com/java-concurrency-thread-directory/ (英文):http://tutoria ...

  6. Java 并发性和多线程

    一.介绍 在过去单 CPU 时代,单任务在一个时间点只能执行单一程序.之后发展到多任务阶段,计算机能在同一时间点并行执行多任务或多进程.虽然并不是真正意义上的“同一时间点”,而是多个任务或进程共享一个 ...

  7. 1、Java并发性和多线程-并发性和多线程介绍

    以下内容转自http://ifeve.com/java-concurrency-thread/: 在过去单CPU时代,单任务在一个时间点只能执行单一程序.之后发展到多任务阶段,计算机能在同一时间点并行 ...

  8. 6、Java并发性和多线程-并发性与并行性

    以下内容转自http://tutorials.jenkov.com/java-concurrency/concurrency-vs-parallelism.html(使用谷歌翻译): 术语并发和并行性 ...

  9. 4、Java并发性和多线程-并发编程模型

    以下内容转自http://ifeve.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E6%A8%A1%E5%9E%8B/: 并发系统可以采用多种并发编程模型来实现. ...

随机推荐

  1. ACM_天涯若比邻(最小与最大相邻素数)

    天涯若比邻 Time Limit: 2000/1000ms (Java/Others) Problem Description: 一心想搞ACM的小G最近迷上了数论,特别对于跟“素数”相关的问题特别有 ...

  2. 锚点、target="page1"、浮标回到顶部(该点未实现,能力不足)

    <html> <head> <meta charset="utf-8"> <title>链接</title> <! ...

  3. 深度优先搜索DFS和广度优先搜索BFS简单解析

    转自:https://www.cnblogs.com/FZfangzheng/p/8529132.html 深度优先搜索DFS和广度优先搜索BFS简单解析 与树的遍历类似,图的遍历要求从某一点出发,每 ...

  4. Java图片上查找图片算法

    之前用按键精灵写过一些游戏辅助,里面有个函数叫FindPic,就是在屏幕范围查找给定的一张图片,返回查找到的坐标位置. 现在,Java来实现这个函数类似的功能. 算法描述: 屏幕截图,得到图A,(查找 ...

  5. java 8 stream使用

    使用stream代替循环的方案 1.定义一个Article类包括标题.作者.标签 private class Article { private final String title; private ...

  6. STL容器的排序

    STL容器的排序,支持随机访问的容器vector,deque,string没有sort成员,可调用std::sort排序:list排序调用自带的list::sort. 下面是std::sort函数,有 ...

  7. Java基础(四)--接口和抽象类

    接口和抽象类能够体现OOP的抽象,而接口和抽象类也是日常开发中经常用到的 抽象方法: 抽象方法就是被abstract修饰的方法,只有声明,没有实现,也就是没有方法体 public abstract v ...

  8. 汇编学习pushl, popl

  9. JPA 与 JDBC 的区别和基本用法

    JPA 概念 JPA(Java Persistence API)用于对象持久化的 API,是 Java EE 5.0 平台标准的 ORM 规范,使得应用程序以统一的方式访问持久层. 与 JDBC 的对 ...

  10. The King’s Ups and Downs(HDU 4489,动态规划递推,组合数,国王的游戏)

    题意: 给一个数字n,让1到n的所有数都以波浪形排序,即任意两个相邻的数都是一高一低或者一低一高 比如:1324   4231,再比如4213就是错的,因为4高,2低,接下来1就应该比2高,但是它没有 ...