正常的锁(比如lock)一般都是用来只允许一个任务访问一项资源,而*计数信号量(Semaphore)*允许最多n个任务同时访问这个资源,常常被用来做流控。
信号量在使用的过程中总结有如下几点需要注意的:
- 想要执行必须要能通过
acquire()
相关方法获取许可证,否则就会被阻塞,见示例1 acquire
和release
没有绝对的先后顺序,release
可以先于acquire
执行,且许可证的数量和初始化时么关系,见示例2acquireUninterruptibly
方法相对于acquire
方法主要是及时是耗时的操作也不会中断,必须执行完,所以一般生产环境推荐使用acquire
,避免长时间占用线程- 在实例化的时候可以指定公平锁或者非公平锁,两者区别,见示例1:
- 公平:即根据先来后到的顺序给阻塞队列中的线程颁发许可证
- 非公平:给任意一个线程颁发许可证
示例1
6个雇员,只有3个坑位,所以只能最多三个人登坑
package com.demos.semaphore;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SimpleSemaphoreTest {
public static void main(String[] args) {
// Semaphore pits = new Semaphore(3);
Semaphore pits = new Semaphore(3,true); // 使用公平锁,实例化3个坑位
new Thread(new Employee(pits, "雇员1")).start();
new Thread(new Employee(pits, "雇员2")).start();
new Thread(new Employee(pits, "雇员3")).start();
new Thread(new Employee(pits, "雇员4")).start();
new Thread(new Employee(pits, "雇员5")).start();
new Thread(new Employee(pits, "雇员6")).start();
}
}
class Employee implements Runnable {
private Semaphore semaphore;
private String name;
public Employee(Semaphore semaphore, String name) {
this.semaphore = semaphore;
this.name = name;
}
@Override
public void run() {
try {
this.semaphore.acquire();
System.out.println(this.name + " 开始登坑...");
TimeUnit.SECONDS.sleep(2);
System.out.println(this.name + " 结束登坑...");
this.semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 使用公平锁
雇员2 开始登坑...
雇员1 开始登坑...
雇员3 开始登坑...
雇员3 结束登坑...
雇员2 结束登坑...
雇员1 结束登坑...
雇员5 开始登坑...
雇员6 开始登坑...
雇员4 开始登坑...
雇员5 结束登坑...
雇员6 结束登坑...
雇员4 结束登坑...
// 非公平锁随机分配
雇员2 开始登坑...
雇员1 开始登坑...
雇员6 开始登坑...
雇员1 结束登坑...
雇员6 结束登坑...
雇员3 开始登坑...
雇员2 结束登坑...
雇员5 开始登坑...
雇员4 开始登坑...
雇员5 结束登坑...
雇员3 结束登坑...
雇员4 结束登坑...
示例2
package com.demos.semaphore;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class TestSemaphore2 {
public static void main(String[] args) {
int permitsNum = 2;
final Semaphore semaphore = new Semaphore(permitsNum);
try {
System.out.println("availablePermits:" + semaphore.availablePermits());
// 未执行acquire先执行了release
semaphore.release(2);
// 此时打印可用许可证是4个,表示和初始化值没关系
System.out.println("availablePermits:" + semaphore.availablePermits() + ",semaphore.tryAcquire(4, 1, TimeUnit.SECONDS):" + semaphore.tryAcquire(4, 1, TimeUnit.SECONDS));
} catch (Exception e) {
}
}
}