Semaphore并发资源控制器的使用场景
Semaphore可以理解为信号量,用于控制资源能够被并发访问的线程数量,以保证多个线程能够合理的使用特定资源。Semaphore就相当于一个许可证,线程需要先通过acquire方法获取该许可证,该线程才能继续往下执行,否则只能在该方法出阻塞等待。当执行完业务功能后,需要通过release()
方法将许可证归还,以便其他线程能够获得许可证继续执行。
Semaphore可以用于做流量控制,特别是公共资源有限的应用场景,比如数据库连接。假如有多个线程读取数据后,需要将数据保存在数据库中,而可用的最大数据库连接只有10个,这时候就需要使用Semaphore来控制能够并发访问到数据库连接资源的线程个数最多只有10个。在限制资源使用的应用场景下,Semaphore是特别合适的。
package semaphore;
import java.util.concurrent.Semaphore;
/**
* @Author: jiaqing.xu@hand-china.com
* @Date: 2019-03-29 14:28
* @Description
*/
public class SemaphoreService {
/**
* 最多只有一个信号量
* 同一个时间内最多允许1个线程执行acquire和release之间的代码
* 如果该值大于1并不能保证线程的安全性 有可能出现多个线程共同访问实例变量的情况
*/
private Semaphore semaphore = new Semaphore(1);
/**
* 测试方法
*/
public void testMethod() throws InterruptedException {
//获取信号量 参数值 代表许可的数量 这里代表调用一次会获取到1个许可
semaphore.acquire();
System.out.println("The begin thread:" + Thread.currentThread().getName() + " time:" + System.currentTimeMillis());
Thread.sleep(1000);
System.out.println("The end thread:" + Thread.currentThread().getName() + " time:" + System.currentTimeMillis());
//释放信号量
semaphore.release();
}
}
package semaphore;
/**
* @Author: jiaqing.xu@hand-china.com
* @Date: 2019-03-29 14:33
* @Description
*/
public class TestSemaphore {
public static void main(String[] args) throws InterruptedException {
SemaphoreService semaphoreService = new SemaphoreService();
//1.继承Thread
Thread thread1 = new Thread(() -> {
try {
semaphoreService.testMethod();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread1.setName("a");
//2.继承Thread
Thread thread2 = new Thread(() -> {
try {
semaphoreService.testMethod();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread2.setName("b");
thread1.start();
thread2.start();
}
}
执行结果如下图所示:
下面来看下Semaphore的主要方法:
//获取许可,如果无法获取到,则阻塞等待直至能够获取为止
void acquire() throws InterruptedException
//同acquire方法功能基本一样,只不过该方法可以一次获取多个许可
void acquire(int permits) throws InterruptedException
//释放许可
void release()
//释放指定个数的许可
void release(int permits)
//尝试获取许可,如果能够获取成功则立即返回true,否则,则返回false
boolean tryAcquire()
//与tryAcquire方法一致,只不过这里可以指定获取多个许可
boolean tryAcquire(int permits)
//尝试获取许可,如果能够立即获取到或者在指定时间内能够获取到,则返回true,否则返回false
boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException
//与上一个方法一致,只不过这里能够获取多个许可
boolean tryAcquire(int permits, long timeout, TimeUnit unit)
//返回当前可用的许可证个数
int availablePermits()
//返回正在等待获取许可证的线程数
int getQueueLength()
//是否有线程正在等待获取许可证
boolean hasQueuedThreads()
//获取所有正在等待许可的线程集合
Collection<Thread> getQueuedThreads()
另外,在Semaphore的构造方法中还支持指定是够具有公平性,默认的是非公平性,这样也是为了保证吞吐量。
Exchanger线程数据交换器的使用场景
先看下这个例子,了解下exchanger的简单使用:
ThreadA的定义如下:
package extthread;
import java.util.concurrent.Exchanger;
/**
* @Author: jiaqing.xu@hand-china.com
* @Date: 2019-03-29 16:03
* @Description
*/
public class ThreadA extends Thread {
private Exchanger<String> exchanger;
public ThreadA(Exchanger<String> exchanger) {
super();
this.exchanger = exchanger;
}
@Override
public void run() {
try {
System.out.println("在线程A中获取的线程B的值=" + exchanger.exchange("1"));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
ThreadB的定义如下:
package extthread;
import java.util.concurrent.Exchanger;
/**
* @Author: jiaqing.xu@hand-china.com
* @Date: 2019-03-29 16:08
* @Description
*/
public class ThreadB extends Thread {
private Exchanger<String> exchanger;
public ThreadB(Exchanger<String> exchanger) {
super();
this.exchanger = exchanger;
}
@Override
public void run() {
try {
System.out.println("在线程B中获取的线程A的值=" + exchanger.exchange("2"));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package extthread;
import java.util.concurrent.Exchanger;
/**
* @Author: jiaqing.xu@hand-china.com
* @Date: 2019-03-29 16:10
* @Description
*/
public class Main {
public static void main(String[] args) {
Exchanger<String> exchanger = new Exchanger<>();
ThreadA threadA = new ThreadA(exchanger);
ThreadB threadB = new ThreadB(exchanger);
//启动a b线程
threadA.start();
threadB.start();
}
}
在线程B中获取的线程A的值=1
在线程A中获取的线程B的值=2
Process finished with exit code 0
Exchanger是一个用于线程间协作的工具类,用于两个线程间数据能够交换。它提供了一个交换的同步点,在这个同步点两个线程能够交换数据。具体交换数据是通过exchange方法来实现的,如果一个线程先执行exchange方法,那么它会同步等待另一个线程也执行exchange方法,这个时候两个线程就都达到了同步点,两个线程就可以交换数据。
//当一个线程执行该方法的时候,会等待另一个线程也执行该方法,因此两个线程就都达到了同步点
//将数据交换给另一个线程,同时返回获取的数据
V exchange(V x) throws InterruptedException
//同上一个方法功能基本一样,只不过这个方法同步等待的时候,增加了超时时间
V exchange(V x, long timeout, TimeUnit unit)
throws InterruptedException, TimeoutException