`
85977328
  • 浏览: 1899460 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

java并发(十八)信号量

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

本文的涉及的主题如下:

简单的Semaphore实现
使用Semaphore来发出信号
可计数的Semaphore
有上限的Semaphore
把Semaphore当锁来使用
JAVA的信号量接口实现
一、简单的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发出的信号来通知对方
public class Test {

    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore();
        SendingThread sender = new SendingThread(semaphore);
        ReceivingThread receiver = new ReceivingThread(semaphore);
        receiver.start();
        sender.start();

    }
}
public class ReceivingThread extends Thread {
    Semaphore semaphore = null;

    public ReceivingThread(Semaphore semaphore) {
        this.semaphore = semaphore;
    }

    public void run()  {
        while (true) {
            try {
                this.semaphore.release();
            } catch (Exception e) {
            }
            // receive signal, then do something...
        }
    }
}
public class SendingThread extends Thread {
    Semaphore semaphore = null;

    public SendingThread(Semaphore semaphore) {
        this.semaphore = semaphore;
    }

    public void run() {
        while (true) {
            // do something, then signal
            this.semaphore.take();
        }
    }
}

三、可计数的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块中被执行。这样可以保在关键区域的代码抛出异常的情况下,信号也一定会被释放。

六、JAVA的信号量接口实现
Semaphore semaphore = new Semaphore(0);
semaphore.acquire(2); // 获取许可
semaphore.release();//授权
semaphore.release(2);//授权

new Semaphore(0)表示初始状态,semaphore.acquire全部都阻塞,等待授权发放。
semaphore.acquire(2)表示至少需要2个授权才可以放行代码。此时可以调用2次无参方法semaphore.release()或者直接一次性发放2个授权,调用semaphore.release(2)。
分享到:
评论

相关推荐

    java并发编程实战源码,java并发编程实战pdf,Java

    3. **并发控制**:Java提供了多种并发控制工具,包括synchronized、wait()、notify()、notifyAll()、ReentrantLock(可重入锁)、Semaphore(信号量)和CountDownLatch(倒计时器)等。这些工具用于协调不同线程的...

    《java 并发编程实战高清PDF版》

    其中包括`Semaphore`(信号量)用于限制同时访问特定资源的线程数量,`CountDownLatch`(倒计时器)用于一次性阻塞多个线程,直到某个事件发生,以及`CyclicBarrier`(循环栅栏)让一组线程等待其他线程到达特定点后...

    JAVA并发编程艺术pdf版

    - **java.util.concurrent** 包:提供了各种并发工具类,如Semaphore(信号量)、CyclicBarrier(循环屏障)、CountDownLatch(倒计时器)和ExecutorService(线程池)等。 - **Future和Callable**:Future接口...

    java并发编程实践pdf笔记

    - `java.util.concurrent`包提供了一系列的并发工具类,如`Semaphore`(信号量)、`CountDownLatch`(计数器)、`CyclicBarrier`(回环栅栏)等,它们为复杂的并发场景提供了强大的支持。 - **并发集合** 如`...

    java并发编程与实践

    3. **并发工具类**:Java并发包(java.util.concurrent)包含了许多工具类,如Semaphore(信号量)、CyclicBarrier(回环屏障)、CountDownLatch(计数器门锁)和ThreadPoolExecutor(线程池)。这些工具可以帮助...

    Java并发实战

    因此,合理使用同步机制(如锁、信号量、原子变量等)是必须的。 4. 线程间的通信与协作机制,包括生产者-消费者问题、线程间的条件等待与通知等。 5. 避免死锁:合理设计资源分配策略和锁的获取顺序,确保不会出现...

    Java并发编程(23)并发新特性-信号量Semaphor

    信号量(Semaphore)是Java并发库中的一种工具类,它提供了一种控制多个线程对共享资源访问的方式,从而实现高级别的同步。在Java 5引入并发包`java.util.concurrent`后,信号量作为`Semaphore`类被添加,成为并发...

    java并发编程

    4. **Semaphore**: 它是信号量,用于控制同时访问特定资源的线程数量,可以用于实现流量控制或限流。 5. **ReentrantLock**: 这是可重入的互斥锁,比`synchronized`关键字更灵活,支持公平锁和非公平锁,以及可中断...

    Java并发编程从入门到精通(pdf)(附源码)

    接着,书中将深入探讨Java并发工具类,如Executor框架、Semaphore信号量、CyclicBarrier和CountDownLatch等,这些工具在实际项目中有着广泛的应用,学习它们能帮助开发者更好地控制和协调并发任务。 此外,书中的...

    Java进程信号量机制的实验程序

    综上所述,Java进程信号量机制是解决多线程环境中资源管理和同步问题的有效工具,通过Semaphore类提供的API,开发者可以灵活地控制并发程度,避免资源过度竞争,保证程序的正确性和效率。在实际项目中,根据需求选择...

    Java并发编程设计原则和模式

    3. Semaphore:信号量,用于控制对有限资源的访问权限,常用于限流和同步。 六、线程安全与内存模型 1. Java内存模型(JMM):定义了线程之间如何共享和交互数据,以及对内存可见性的保证。 2. volatile、...

    java并发书籍xxxxxxx

    11. **Semaphore信号量**:Semaphore用于控制同时访问特定资源的线程数量,通过acquire()获取一个许可,release()归还许可。 12. **CompletableFuture**:Java 8引入的异步编程工具,可以链式调用,组合多个异步...

    Java并发编程实战

    5.5.3 信号量 5.5.4 栅栏 5.6 构建高效且可伸缩的结果缓存 第二部分 结构化并发应用程序 第6章 任务执行 6.1 在线程中执行任务 6.1.1 串行地执行任务 6.1.2 显式地为任务创建线程 6.1.3 无限制创建线程的...

    Java并发编程从入门到精通源码.rar

    这些工具可以帮助开发者更高效、安全地管理线程,实现线程池、信号量控制、同步屏障等功能。 再者,`java.util.concurrent.atomic`包中的原子类提供了非阻塞的线程安全操作,如`AtomicInteger`、`AtomicLong`等,...

    Java并发工具包

    Java并发工具包中包含了一些同步辅助类,如Semaphore(信号量)、CyclicBarrier(循环屏障)和CountDownLatch(计数器门锁)。它们帮助协调多线程间的协作,控制线程的并发访问数量或等待特定条件。 六、FutureTask...

    Java并发编程学习笔记

    Java并发包(java.util.concurrent)也提供了丰富的并发工具,如线程安全集合(如ConcurrentHashMap)、阻塞队列(如ArrayBlockingQueue)、信号量(Semaphore)、并发集合(如CopyOnWriteArrayList)和各种执行器...

    JAVA并发编程实践

    - **Semaphore**:信号量,用于控制对有限资源的访问。 - **Exchanger**:用于在两个线程之间交换对象。 - **Phaser**:类似CountDownLatch,但可以动态注册和注销参与者。 ### 三、Java并发编程最佳实践 #### 3.1...

    JAVA并发编程实战.pdf

    - **使用信号量Semaphore**:利用信号量控制生产和消费的次数,从而达到同步的目的。 #### 2. 银行账户转账示例 - **账户余额一致性问题**:解决多个线程同时进行转账时可能发生的账户余额不一致的问题。 - **使用...

    java并发编程艺术

    3. **并发工具类**:介绍Java并发包(java.util.concurrent)中的各种工具类,如Semaphore信号量、CountDownLatch倒计时器、CyclicBarrier回环栅栏、ThreadPoolExecutor线程池等,以及如何在实际编程中合理使用它们...

    java并发编程技术

    4. **并发包JUC(Java Util Concurrency)**:JUC是Java提供的高级并发工具包,包含如Semaphore(信号量)、CountDownLatch(计数器)、CyclicBarrier(回环栅栏)、Exchanger(交换器)等工具类,它们简化了多线程...

Global site tag (gtag.js) - Google Analytics