Semaphore(信号量)通常用于限制访问某些(物理或逻辑)资源线程的数量。
一个信号量维护一组许可证。每个acquire()都会阻塞,直到获得许可证;每个release()会释放许可证。如果没有线程需要获取许可证;信号量只是像其它池对象那样保持一个可用许可证的计数。
池对象:线程池、数据库连接池等,创建一个池子,池子中已经建立好了连接,需要使用的时候直接从池子中拿来用。
一、Pool类源代码
例子,这里有一个Pool类,它使用信号量来控制对项目池的访问
package ThreadStudy;
import java.util.concurrent.Semaphore;
//信号量通常用于限制访问某些(物理或逻辑)资源线程的数量
public class Pool {
private final static int MAX_AVAILABLE = 100;
//创建一个具有100许可证的信号量,采用公平方式获取许可证
private final Semaphore semaphore = new Semaphore(MAX_AVAILABLE, true);
public Object getItem() throws Exception {
semaphore.acquire();
return getNextAvailableItem();
}
public void putItem(Object x) {
if (markAsUnused(x)) {
semaphore.release();
}
}
// 管理的任何类型的项目
Object[] items = new Object[MAX_AVAILABLE];
boolean[] used = new boolean[MAX_AVAILABLE];
protected synchronized Object getNextAvailableItem() {
for (int i = 0; i < MAX_AVAILABLE; i++) {
if (!used[i]) {
used[i] = true;
return items[i];
}
}
return null;
}
protected synchronized boolean markAsUnused(Object item) {
for (int i = 0; i < MAX_AVAILABLE; ++i) {
if (item == items[i]) {
if (used[i]) {
used[i] = false;
return true;
} else {
return false;
}
}
}
return false;
}
}
二、二进制信号量用法
一个初始化为1的信号量,并且它最多只能有一个permit可用,可以作为互斥锁。这通常被称为二进制信号量,因为它只有两种状态:一个可用的许可证,或零可用的许可证。当以这种方式使用时,二进制信号量具有这样的属性(与许多锁实现不同),即“锁”可以由所有者以外的线程释放(因为信号量没有所有权的概念)。这在某些特定的上下文中可能很有用,比如死锁恢复。
package ThreadStudy;
import java.util.concurrent.Semaphore;
public class SemaphoreT2 {
public static void main(String[] args) throws Exception {
Semaphore semophore = new Semaphore(1);
semophore.acquire();
new Thread(new Runnable() {
@Override
public void run() {
try {
// 获取不到许可证,线程阻塞
System.out.println(Thread.currentThread().getName()+"获取不到许可证,线程阻塞");
semophore.acquire();
System.out.println(Thread.currentThread().getName()+"获取到许可证,线程继续执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
// 释放许可证
System.out.println(Thread.currentThread().getName()+"释放许可证");
Thread.sleep(3000);
semophore.release();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
}
三、Semaphore类中各种方法使用介绍
1、俩个构造方法的区别,公平非公平!
Semaphore(int permits) | 创建一个具有给定许可数和不公平设置的信号量 |
Semaphore(int permits, boolean fair) | 创建一个具有给定许可数和给定公平性设置的信号量 |
2、普通方法
获取许可证 | 释放许可证 |
acquire() 从这个信号量获得一个许可,阻塞直到一个可用,或者线程被中断 | release() 释放一个许可证,将其返回给信号量 |
acquire(int permits) 从信号量获取给定数量的许可 | release(int permits) 释放给定的许可证数量,将它们返回给信号量 |
从信号量获得一个许可,阻塞直到一个可用 | |
acquireUninterruptibly(int permits) 从信号量获取给定数量的许可 | |
获取到许可证返回true,获取不到返回false | |
tryAcquire(int permits) | |
tryAcquire(int permits, long timeout, TimeUnit unit) | |
tryAcquire(long timeout, TimeUnit unit) |
3、代码示例
package ThreadStudy;
import java.util.concurrent.Semaphore;
public class SemaphoreT1 {
public static void main(String[] args) {
int MAX = 10;
// 创建10个许可证
Semaphore s = new Semaphore(MAX);
for (int i = 0; i < MAX; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("等待获取许可证的线程数量评估值:" + s.getQueueLength());
// 获取二个许可证
s.acquire(2);
System.out.println("返回此信号量中可用的当前许可数,此方法通常用于调试和测试目的:" + s.availablePermits());
System.out.println("获取并返回所有立即可用的许可:" + s.drainPermits());
// 和acquire的唯一区别就是,一直处于阻塞状态,除了获取到一个许可证
s.acquireUninterruptibly();
// 获取许可证,并且返回true,便于代码流程控制
if (s.tryAcquire()) {
System.out.println("tryAcquire返回为true");
} else {
System.out.println("tryAcquire返回为false");
}
Thread.sleep(1000);
// 获取一个许可证
s.acquire();
// 获取5个许可证
s.acquire(5);
System.out.println("线程获取到许可:" + Thread.currentThread().getName());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
}
}
}