控制并发流程的工具类,作用就是帮助我们更容易得让线程之间合作,让线程之间相互配合,来满足业务逻辑
countDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。
是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了
只提供了一个构造方法,参数count为需要倒数的数值
调用await()方法的线程将会被挂起,它会等待直到count的值为0才继续执行
没调用一次数值减1,直到为0时,等待的线程会被唤醒
public class countDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
CountDownLatch downLatch = new CountDownLatch(5);
ExecutorService service = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
int finalI = i;
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
Thread.sleep((long)Math.random()*100000);
System.out.println("NO "+ finalI + "检查完成");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
downLatch.countDown();
}
}
};
service.submit(runnable);
}
System.out.println("等待5个人检查完成");
downLatch.await();
System.out.println("检查完成");
}
}
模拟跑步的场景,5个人等待裁判员的发令枪。只要一声令下,所有人同时开始跑步
public class countDownLatchDemo2 {
public static void main(String[] args) throws InterruptedException {
CountDownLatch downLatch = new CountDownLatch(1);
ExecutorService service = Executors.newFixedThreadPool(15);
for (int i = 0; i < 15; i++) {
int finalI = i;
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
System.out.println("NO "+ finalI + "开始等待发令");
downLatch.await();
System.out.println("NO "+ finalI + "开始跑步");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
service.submit(runnable);
}
Thread.sleep(5000);
System.out.println("发令枪响起,开始跑步");
downLatch.countDown();
}
}
模拟跑步的场景,5个人等待裁判员的发令枪。只要一声令下,所有人同时开始跑步. 终点的裁判员等待最后一名运动员到达终点后,宣布运动会结束
public class countDownLatchDemo2 {
public static void main(String[] args) throws InterruptedException {
CountDownLatch downLatch = new CountDownLatch(1);
CountDownLatch downLatch2 = new CountDownLatch(15);
ExecutorService service = Executors.newFixedThreadPool(15);
for (int i = 0; i < 15; i++) {
int finalI = i;
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
System.out.println("NO "+ finalI + "开始等待发令");
downLatch.await();
System.out.println("NO "+ finalI + "开始跑步");
Thread.sleep((long)Math.random()*10000);
downLatch2.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
service.submit(runnable);
}
Thread.sleep(5000);
System.out.println("发令枪响起,开始跑步");
downLatch.countDown();
System.out.println("裁判员等待");
downLatch2.await();
System.out.println("最后一名运行员到达,比赛结束");
}
}
用来限制和管理优先资源的使用情况
信号量的作用是维护一个"许可证"的计数,线程获取许可证,那许可证的剩余就减一,线程也可以释放一个许可证,那么许可证数量就加一,当信号量拥有的许可证的数量为0时,那么下一个获取许可证的线程将会等待,直到另外的线程释放了许可证
初始化Semaphore 并指定许可证数量
在需要被限制的代码前acquire() 或者acquireUninterruptibly()方法
任务结束后,调用release()释放许可证
这里可以设置是否要使用公平策略,如果传入true,那么semaphore会把之前等待的线程放到FIFO的队列里,以便于当有了新的许可证可以分发给之前等了最长时间的线程
一次拿到指定数量的许可证
看看现在有没有空闲的许可证,如果有的话就获取,如果没有的话没关系,不比陷入阻塞,可以去做别的是事,过会再来查看许可证的空闲情况
和tryAcquire()一样,但是多了一个超时时间,比如“在3秒内获取不到许可证,我就去做别的事”
释放许可证
释放指定数量的许可证
public class SemaphoreDemo {
static Semaphore se = new Semaphore(3,true);
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(50);
for (int i = 0; i < 100; i++) {
service.submit(new Task());
}
service.shutdown();
}
static class Task implements Runnable{
@Override
public void run() {
try {
se.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 拿到许可证");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+" 释放许可证");
se.release();
}
}
}
获取和释放的许可证必须是一致,否则会导致许可证数量不够用,会导致程序卡死
并不是必须由获取许可证的线程释放那个许可证,事实上,获取和释放许可证对线程并无要求,也是A获取了,然后由B释放,只要逻辑合理即可。
当线程1需要等待某个条件的时候,它就去执行condition.await()方法,一旦执行了await()方法,线程就会进入阻塞状态
然后通常会有另外一个线程,去执行对应的条件,直到这个条件达成的时候,调用condition.signal()方法,这是JVM就会从被阻塞的线程栈里面找到被阻塞的线程,当线程1就会收到可执行信号的时候,线程的状态就会变成Runnable可执行状态
唤起所有的正在等待的线程
只唤起等待时间最长的线程
public class ConditionDemo {
private ReentrantLock reentrantLock = new ReentrantLock();
private Condition condition = reentrantLock.newCondition();
void method1() throws InterruptedException {
reentrantLock.lock();
try{
System.out.println("条件不满足");
condition.await();
System.out.println("条件满足,开始执行");
}finally {
reentrantLock.unlock();
}
}
void method2() throws InterruptedException {
reentrantLock.lock();
try{
System.out.println("准备工作完成,开始唤醒其他线程");
condition.signal();
}finally {
reentrantLock.unlock();
}
}
public static void main(String[] args) {
ConditionDemo conditionDemo = new ConditionDemo();
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
conditionDemo.method1();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Runnable runnable1 = new Runnable() {
@Override
public void run() {
try {
conditionDemo.method2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
new Thread(runnable).start();
new Thread(runnable1).start();
}
}
public class ConditionDemo2 {
private static Integer queueSize = 10;
private static PriorityQueue<Integer> queue = new PriorityQueue<>(queueSize);
private ReentrantLock reentrantLock = new ReentrantLock();
private Condition notEmpty = reentrantLock.newCondition();
private Condition notFull = reentrantLock.newCondition();
public static void main(String[] args) {
ConditionDemo2 cd = new ConditionDemo2();
producer producer = cd.new producer();
consumer cs = cd.new consumer();
producer.start();
cs.start();
}
class producer extends Thread {
@Override
public void run() {
while(true){
try {
reentrantLock.lock();
if(queue.size() == queueSize){
System.out.println("队列已满");
try {
notFull.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
queue.offer(1);
notEmpty.signal();
}finally {
reentrantLock.unlock();
}
}
}
}
class consumer extends Thread {
@Override
public void run() {
while(true){
try{
reentrantLock.lock();
if(queue.size() == 0){
System.out.println("队列为空");
try {
notEmpty.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(queue.poll());
notFull.signal();
}finally {
reentrantLock.unlock();
}
}
}
}
}
CyclicBarrier循环栅栏和CountDownLatch很类似,都能阻塞一组线程
当有大量线程相互配合,分别计算不同任务,并且需要最后统一汇总的时候,我们可以使用CyclicBarrier。CyclicBarrier可以构造一个集结点,当某一个线程执行完毕,他就会到集结点等待,知道所有线程都到了集结点,那么栅栏就被撤销,所有线程在统一出发,继续执行剩下的任务。
public class CyclicBarrierDemo {
public static void main(String[] args) {
CyclicBarrier cb = new CyclicBarrier(5, new Runnable() {
@Override
public void run() {
System.out.println("人全部都到了");
}
});
for (int i = 0; i < 5; i++) {
new Thread(new Task(i,cb)).start();
}
}
static class Task implements Runnable{
private int id;
private CyclicBarrier cyclicBarrier;
public Task(int id, CyclicBarrier cyclicBarrier) {
this.id = id;
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
System.out.println("线程 "+id+" "+"前往集合地点");
try {
Thread.sleep(5000);
System.out.println("线程 "+id+" "+"到达集合地点,等到其他人");
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
}
/**
线程 0 前往集合地点
线程 1 前往集合地点
线程 2 前往集合地点
线程 3 前往集合地点
线程 4 前往集合地点
线程 2 到达集合地点,等到其他人
线程 0 到达集合地点,等到其他人
线程 3 到达集合地点,等到其他人
线程 1 到达集合地点,等到其他人
线程 4 到达集合地点,等到其他人
人全部都到了
*/
作用不同
CyclicBarrier要等到固定数量的线程都达到了栅栏位置才能继续执行,而CountDownLatch只需要等待数字到0 。CountDownLatch用于事件,CyclicBarrier用于线程
可重用性
CountDownnLatch在倒数到0并触发门闩打开后,就不能再次使用了,除非新建新的实例,而CyclicBarrier可以重复使用