支持博主:点赞、收藏⭐、留言
Semaphore也叫信号量,在JDK1.5被引入,位于java.util.concurrent包中,可以用来控制同时访问特定资源的线程数量,通过协调各个线程,以保证合理的使用资源。
Semaphore可以看作是synchronized关键字的升级版本,能够控制线程并发的数量,这一点是synchronized关键字做不到的。
内部维护了一个计数器,可加可减,acquire()方法是做减法,release()方法是做加法,具体内容见第三章节。
该部分API我们在之后的章节中会详细讲解,读者可以先大致看一下,之后需要用到此处API的时候可以进行查阅。
Semaphore类的结构视图:
官方API方法描述具体如下:
void acquire()
从该信号量获取许可证,阻止直到可用,或线程为 interrupted 。
void acquire(int permits)
从该信号量获取给定数量的许可证,阻止直到所有可用,否则线程为 interrupted 。
void acquireUninterruptibly()
从这个信号灯获取许可证,阻止一个可用的。
void acquireUninterruptibly(int permits)
从该信号量获取给定数量的许可证,阻止直到所有可用。
int availablePermits()
返回此信号量中当前可用的许可数。
int drainPermits()
获取并返回所有可立即获得的许可证。
protected Collection getQueuedThreads()
返回一个包含可能正在等待获取的线程的集合。
int getQueueLength()
返回等待获取的线程数的估计。
boolean hasQueuedThreads()
查询任何线程是否等待获取。
boolean isFair()
如果此信号量的公平设置为真,则返回 true 。
protected void reducePermits(int reduction)
缩小可用许可证的数量。
void release()
释放许可证,将其返回到信号量。
void release(int permits)
释放给定数量的许可证,将其返回到信号量。
String toString()
返回一个标识此信号量的字符串及其状态。
boolean tryAcquire()
从这个信号量获得许可证,只有在调用时可以使用该许可证。
boolean tryAcquire(int permits)
从这个信号量获取给定数量的许可证,只有在调用时全部可用。
boolean tryAcquire(int permits, long timeout, TimeUnit unit)
从该信号量获取给定数量的许可证,如果在给定的等待时间内全部可用,并且当前线程尚未 interrupted 。
boolean tryAcquire(long timeout, TimeUnit unit)
如果在给定的等待时间内可用,并且当前线程尚未 到达 interrupted,则从该信号量获取许可。
构造方法:
acquire()方法:
release()方法:
因为我们在构造器中控制了并发的线程数量为一,所以当我们创建三个线程,同时启动,他们之间不会轮流着去执行,只能按照先后顺序执行完了前面一个才能执行后面的线程,具体代码如下:
SemaphoreDemo01类:
import java.util.concurrent.Semaphore;
public class SemaphoreDemo01 {
// 1.构造函数的permits参数是许可的意思,初始化permits(许可)的数量,代表同一时间内,最多允许多少个线程同时执行acquire()和release()之间的代码,具体看需要用多少个证书
private Semaphore semaphore = new Semaphore(1);
public void testMethodDemo() {
try {
// 2.无参数的acquire()作用是使用1个permits(许可),是减法操作,即减去对应的permits(许可);
// 3.有参数的acquire(),可以指定减去多少个permits(许可)数量。
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "开始时间:" + System.currentTimeMillis());
Thread.sleep(10000);
System.out.println(Thread.currentThread().getName() + "结束时间:" + System.currentTimeMillis());
// 4.无参数的release()方法会加上1个permits(许可),是加法操作,即加上对应的permits(许可);
// 5.有参数的release()方法,可以动态添加permits(许可),比如new Semaphore(2),之后我们可以通过release(2),把permits(许可)数量变成4,这个可以说明构造方法中中的2并不是最终的permits(许可)数量,而只是初始数量。
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
ThreadDemo类如下:
public class ThreadDemo extends Thread {
private SemaphoreDemo01 semaphoreDemo01;
public ThreadDemo(SemaphoreDemo01 semaphoreDemo01) {
super();
this.semaphoreDemo01 = semaphoreDemo01;
}
@Override
public void run() {
semaphoreDemo01.testMethodDemo();
}
public static void main(String[] args) {
SemaphoreDemo01 semaphoreDemo01 = new SemaphoreDemo01();
ThreadDemo a = new ThreadDemo(semaphoreDemo01);
ThreadDemo b = new ThreadDemo(semaphoreDemo01);
ThreadDemo c = new ThreadDemo(semaphoreDemo01);
a.setName("A");
b.setName("B");
c.setName("C");
a.start();
b.start();
c.start();
}
}
运行结果:(线程按照先后顺序执行):
A开始时间:1648477661788
A结束时间:1648477671799
B开始时间:1648477671799
B结束时间:1648477681809
C开始时间:1648477681809
C结束时间:1648477691810
Process finished with exit code 0
此时我们只需要把3.1中SemaphoreDemo01类中的new Semaphore(1);改成new Semaphore(2);。如下:
package com.ysw.concurrent.ConcurrentProgramming.semaphoreAndExchanger;
import java.util.concurrent.Semaphore;
public class SemaphoreDemo01 {
// 1.构造函数的permits参数是许可的意思,代表同一时间内,最多允许多少个线程同时执行acquire()和release()之间的代码
private Semaphore semaphore = new Semaphore(2);
public void testMethodDemo() {
try {
// 2.无参数的acquire()作用是使用1个permits(许可),是减法操作,即减去对应的permits(许可);
// 3.有参数的acquire(),可以指定减去多少个permits(许可)数量。
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "开始时间:" + System.currentTimeMillis());
Thread.sleep(10000);
System.out.println(Thread.currentThread().getName() + "结束时间:" + System.currentTimeMillis());
// 4.无参数的release()方法会加上1个permits(许可),是加法操作,即加上对应的permits(许可);
// 5.有参数的release()方法,可以动态添加permits(许可),比如new Semaphore(2),之后我们可以通过release(2),把permits(许可)数量变成4,这个可以说明构造方法中中的2并不是最终的permits(许可)数量,而只是初始数量。
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
B开始时间:1648478040379
A开始时间:1648478040379
A结束时间:1648478050379
B结束时间:1648478050379
C开始时间:1648478050379
C结束时间:1648478060387
Process finished with exit code 0
ps:当构造方法中传入的permits大于1时,该类并不能保证线程的安全性,因为此时还是可能会出现多个线程共同访问实例变量,导致出现脏数据的情况
release()方法可以动态的添加permits(许可)数量,构造方法中的permits(许可)并不是最终的permits(许可)数量,而只是初始数量。代码如下:
import java.util.concurrent.Semaphore;
public class ReleaseMethodDemo {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(8);
try {
System.out.println(semaphore.availablePermits());
semaphore.acquire();
semaphore.acquire(2);
System.out.println(semaphore.availablePermits());
semaphore.release();
semaphore.release(2);
semaphore.release(3);
System.out.println(semaphore.availablePermits());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
8
5
11
Process finished with exit code 0
Semaphore有两种策略
在上面的3.1中我们提到过Semaphore的构造方法
具体类图如下:
简化后的结果如下:
由类图可以看出,Semaphore中有属性Sync,而Sync类是继承于AQS的,Sync只是对 AQS的一个简单包装,起到了一个修饰作用,我们可以认为Semaphore还是使用AQS实现的。
未完待续…