Java中的信号量
信号量是一种同步辅助工具,可用于线程之间的内部通信或者限制对资源的访问。 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)仅在调用时所有可用的条件下,从此信号量获取给定数量的许可。