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)仅在调用时所有可用的条件下,从此信号量获取给定数量的许可。

