Java中的信号量

时间:2020-01-09 10:35:10  来源:igfitidea点击:

信号量是一种同步辅助工具,可用于线程之间的内部通信或者限制对资源的访问。 Java中的Semaphore实现可直接使用,它是java.util.concurrent包的一部分。

计数信号量

Java并发包中易于使用的信号量的实现是计数信号量。

从概念上讲,信号量维护一组许可证。创建信号量时,将以给定数量的许可创建信号量。可以使用以下步骤来解释Java中信号量的工作

  • 想要访问共享资源的线程尝试使用acquire()方法获取许可。

  • 如果允许,或者换句话说,如果信号量计数大于零,则线程获取许可,否则线程被阻塞。

  • 每次成功获得许可证,计数都会减少。如果计数变为零,则不能给予许可。

  • 当线程使用共享资源完成操作时,它可以使用release()方法释放获得的许可。这将增加信号量的计数。

  • 一旦计数大于零,任何等待获取许可的阻塞线程都可以获取许可。

Java信号量构造函数

  • 信号量(int许可)使用给定数量的许可和不公平的公平性设置来创建信号量。

  • 信号量(int许可,布尔公平)创建具有给定许可数量和给定公平性设置的信号量。

Java中的信号量示例

假设有一种方法的计算量很大,并且我们想在任何给定时间将对这种方法的访问限制为2个线程。在这种情况下,我们可以使用通过2个许可创建的信号量。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

public class SemaphoreDemo {
  public static void main(String[] args) {
    // Semaphore with 2 permits
    Semaphore s = new Semaphore(2);
    ExecutorService ex = Executors.newFixedThreadPool(4);
    // Executing 6 times with a pool of 4 threads
    for(int i = 0; i < 6; i++) {
      ex.execute(new HeavyDuty(s));
    }
    ex.shutdown();
  }
}

class HeavyDuty implements Runnable{
  private Semaphore s;
  HeavyDuty(Semaphore s){
    this.s = s;
  }
  @Override
  public void run() {
    try {
      s.acquire();
      System.out.println("Permit ACQUIRED by " + Thread.currentThread().getName());
      doProcessing();	
      System.out.println("Permit released by " + Thread.currentThread().getName());
      s.release();		
    } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }				
  }
  private void doProcessing() throws InterruptedException{
    System.out.println("doing heavy computation processing ");
    Thread.sleep(5000);
  }
}

输出:

Permit ACQUIRED by pool-1-thread-1
doing heavy computation processing 
Permit ACQUIRED by pool-1-thread-2
doing heavy computation processing 
Permit released by pool-1-thread-1
Permit ACQUIRED by pool-1-thread-4
doing heavy computation processing 
Permit released by pool-1-thread-2
Permit ACQUIRED by pool-1-thread-3
doing heavy computation processing 
Permit released by pool-1-thread-4
Permit ACQUIRED by pool-1-thread-1
doing heavy computation processing 
Permit released by pool-1-thread-3
Permit ACQUIRED by pool-1-thread-2
doing heavy computation processing 
Permit released by pool-1-thread-1
Permit released by pool-1-thread-2

如我们所见,在任何给定时间,许可都是由2个线程获得的。

二进制信号量

Java中只有一个许可的信号灯可以用作互斥锁。这通常被称为二进制信号量,因为它只有两种状态:一个许可可用,或者零许可可用。

Java中的二进制信号量示例

这是一个简单的二进制信号量示例,其中在多个线程之间使用共享计数器。二进制信号量仅允许一个线程在任何给定时间访问共享资源。

public class SemaphoreDemo {
  public static void main(String[] args) {
    // Semaphore with 1 permit
    Semaphore s = new Semaphore(1);
    SharedCounter counter = new SharedCounter(s);
    for(int i = 0; i < 6; i++) {
      new Thread(counter).start();
    }
  }
}

class SharedCounter implements Runnable{
  private int c = 0;
  private Semaphore s;
  SharedCounter(Semaphore s){
   this.s = s;
  }
  @Override
  public void run() {
    try {
      s.acquire();
      incrCounter();
      s.release();	
    }catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
  // incrementing the value
  public void incrCounter()  throws InterruptedException{
    Thread.sleep(10);
    System.out.println("Value for Thread After increment - " + 
        Thread.currentThread().getName() + " " + ++c);
  }
}

输出:

Value for Thread After increment - Thread-0 1
Value for Thread After increment - Thread-1 2
Value for Thread After increment - Thread-2 3
Value for Thread After increment - Thread-3 4
Value for Thread After increment - Thread-4 5
Value for Thread After increment - Thread-5 6

要了解线程如何产生干扰,我们可以在run()方法中使用acquire和release方法进行注释。

public void run() {
  try {
    //s.acquire();
    incrCounter();
    //s.release();	
  }catch (InterruptedException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
  }
}

注释后运行将为以下运行提供以下输出,其中两个线程显示相同的计数。

Value for Thread After increment - Thread-4 1
Value for Thread After increment - Thread-2 2
Value for Thread After increment - Thread-0 3
Value for Thread After increment - Thread-5 4
Value for Thread After increment - Thread-3 1
Value for Thread After increment - Thread-1 2

Java Semaphore类中的方法

Java中的Semaphore类中的一些重要方法如下:

  • acquisition()从此信号量获取一个许可,阻塞直到一个可用,或者线程被中断。

  • acquisition(int permits)从该信号量获取给定数量的许可,阻塞直到所有许可都可用或者线程被中断。

  • availablePermits()返回此信号量中当前可用的许可数量。

  • rainPermits()获取并返回所有立即可用的许可证,或者如果可用否定许可证,则释放它们。

  • getQueuedThreads()返回一个包含可能正在等待获取的线程的集合。

  • isFair()如果此信号量的公平性设置为true,则返回true。

  • release()释放许可,将其返回到信号量。

  • tryAcquire()仅在调用时可用时才从此信号量获取许可。

  • tryAcquire(int permits)仅在调用时所有可用的条件下,从此信号量获取给定数量的许可。