【22】 信号量


信号量作为一种线程同步机制,可以用于解决线程间信号丢失问题,也可以当作Lock来保护临界区使用。 Java从5开始在java.util.concurrent包中引入信号量实现。 虽然,不用再自己实现一个信号量,但是还是那句话:只有知道背后的机制才能更好的使用。

Java 5 提供了一个java.util.concurrent.Semaphore实现类,更多信息,可以参见Jakob Jenkov的博客文章

简单的信号量

先来看看一个简单的信号量实现:

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()方法会等待这个信号。 当接收到信号后,会重置信号状态,然后release()方法退出。

使用这样的信号量可以自然的避免信号量丢失问题。 可以使用take()方法替代notify(),使用release()替代wait()。 如果take()先于release()被调用,而由于信号是存储在内部signal变量上的,所以release()就会知道前面已经调用过take()了。 这样就避免了直接使用wait()notify()时,存在的信号丢失问题。

不过用take()release()来表述信号量发送,似乎不怎么贴切。 从这个命名上来看,其实更贴近锁的概念,稍后会介绍将信号量当作锁来使用。

使用信号量来发送信号

再来看看在两个线程之间通过信号量发送信号的例子:

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实现,并没有对take()方法发送的信号进行计数。 下面再来看看计数信号量的实现:

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--;
  }

}

有界信号量

上面的CoutingSemaphore并没有对信号量数量进行限定。 我们可以在信号量数量上进行一个限定:

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();
  }
}

注意,现在take()方法会在信号量计数到达上限的时候,进行阻塞。 如果到达上限,那么直到release()方法被某个线程调用后,阻塞的take()才能够继续投递信号。

将信号量当作锁来使用

可以使用一个上限为1的信号量当作锁来使用。 这样就可以用take()release()来保护临界区。

BoundedSemaphore semaphore = new BoundedSemaphore(1);

...

semaphore.take();

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

由于只能有一个线程能够获得信号,其他线程都只能阻塞等待。

也可以使用信号量的数量来允许多个线程同时进入临界区。 如果在上述例子中设置信号量限制数为5,那就会允许5个线程同时进入临界区。 如果这样,就需要确保5个线程不会互相冲突 。

最后,需要注意几个地方:

  1. 要确保release()在finally块中调用
  2. 需要注意,将信号量当作锁使用时,相当于不可重入锁

转载于:https://my.oschina.net/roccn/blog/1580003

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值