目录
2.1 acquireUninterruptibly() 和 acquireUninterruptibly(int permits) 方法
2.2 drainPermits() 和 availablePermits() 方法
2.3 getQueueLength() 和 hasQueuedThreads() 方法
1 初识Semaphore
Semaphore类是Concurrent包下的并发编程类,功能是并发限流,比如有很多线程要访问某个资源,但是由于计算能力有限,如果这些线程全部都去获取资源的话,系统会崩溃的,那么此时可以限制同时访问资源的线程数,这样减轻压力。
Semaphore semaphore = new Semaphore(int permits); //创建此类,初始化许可证数量。
public void acquire(); 获取一个许可证。
public void release(); 释放一个许可证。
public void acquire(int permits); 获取permits 个许可证。
public void release(int permits); 释放permits 个许可证。
许可证数量有限,必须先获取许可证,才能继续执行代码,如果许可证已经被用完了,就阻塞等待,直到有线程释放了许可证,再去获取许可证,执行完后,还要释放许可证(归还许可证)。
举个例子去说明,1个图书馆,3个学生,由于图书馆只有2个座位,因此任何时刻,只允许最多2个学生同时在图书馆读书,只有等某个学生读完了离开了,其它的学生才能去拿到座位读书。这种情况就是典型的限流。
图书馆类:
public class Library {
private Semaphore semaphore = new Semaphore(2);
public void ReadBook(int time) throws InterruptedException {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "正在读.....");
Thread.sleep(time);
System.out.println(Thread.currentThread().getName() + "已经读完了");
semaphore.release();
}
}
学生类:
public class Student extends Thread {
private Library library;
private int time;
public Student(Library library, int time){
this.library = library;
this.time = time;
}
@Override
public void run() {
super.run();
try {
library.ReadBook(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
测试代码:
public class CreateTest {
public static void main(String[] args){
Library library = new Library();
/**
* 学生1读书6秒
*/
Student studentOne = new Student(library, 6000);
/**
* 学生2读书2秒
*/
Student studentTwo = new Student(library, 2000);
/**
* 学生3读书1秒
*/
Student studentThree = new Student(library, 1000);
studentOne.setName("学生1");
studentTwo.setName("学生2");
studentThree.setName("学生3");
studentOne.start();
studentTwo.start();
studentThree.start();
}
}
运行结果:
1.1 动态增加许可证数量
一般初始化了许可证的数量,就不再改变了,但是有时需要动态地增加许可证,怎么办呢?
正常情况下,我们获取了多少个许可证,用完之后,就会释放掉多少个许可证。但是假如我们获取了2个许可证,但是释放了3个许可证,那么许可证的数量就会增加1。因此,动态增加许可证的方法就是:执行更多的release()方法。下面举例说明:
public Semaphore semaphore = new Semaphore(2);
public static void main(String[] args){
CreateTest createTest = new CreateTest();
try {
createTest.semaphore.acquire();
System.out.println("可用的许可证数量:" + createTest.semaphore.availablePermits());
createTest.semaphore.release();
createTest.semaphore.release();
System.out.println("可用的许可证数量:" + createTest.semaphore.availablePermits());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
运行结果:
1.2 公平和非公平方式获取许可证
public Semaphore(int permits, boolean fair); // fair参数是用于设置公平还是非公平方式,true表示公平,false表示非公平。
公平方式:即终生平等,先来先得。
非公平方式:可能会出现抢占情况,因此不是公平的,默认使用非公平方式,效率比公平方式高。
2 常用方法
2.1 acquireUninterruptibly() 和 acquireUninterruptibly(int permits) 方法
public void acquireUninterruptibly(); //功能上与acquire()类似,用于获取一个许可证,不同之处在于,如果线程用此方法去获取许可证时,发现没有了,就会阻塞,阻塞期间不允许被中断。
public void acquireUninterruptibly(int permits); //获取permits个许可证。
2.2 drainPermits() 和 availablePermits() 方法
public int drainPermits(); //获取可用的许可证数量,但是获取后会将可用许可证的数量置为0。
public int availablePermits(); //仅仅是获取可用的许可证数量。
Semaphore semaphore = new Semaphore(2);
semaphore.acquire();
System.out.println("可用的许可证数量:" + semaphore.drainPermits());
System.out.println("可用的许可证数量:" + semaphore.availablePermits());
运行结果:
2.3 getQueueLength() 和 hasQueuedThreads() 方法
public final int getQueueLength(); //返回等待获取许可证的线程数量。
public final boolean hasQueuedThreads(); //是否有线程在等待获取许可证。
2.4 尝试获取许可证
1、public boolean tryAcquire(); //尝试获取1个许可证,获取成功就返回true,如果获取不到,就返回false,不阻塞。
2、public boolean tryAcquire(long timeout, TimeUnit unit); //unit是时间单位,timeout是时间长度,在此时间内尝试去获取1个许可证,如果成功就返回true,失败返回false。
3、public boolean tryAcquire(int permits); //尝试获取permits个许可证,成功返回true,失败返回false,不阻塞。
4、public boolean tryAcquire(int permits, long timeout, TimeUnit unit); //尝试在规定时间内获取permits个许可证,成功返回true,失败返回false。