CountDownLunch、Semaphore
场景:主线程运行,到一定点之后,由于任务比较重,则线程执行,同时可以执行的线程数为n,等待每个线程都执行完毕后主线程继续往下执行;
CountDownLunch即可实现计数操作,等待所有线程执行完毕后主线程开始执行;
Semaphore即可控制共享资源的最大线程数,通过semaphore.acquire()来获取访问许可;通过semaphore.release()来释放许可;
简单的理解就是:比如百米接力赛,共有五个跑道,三组接力赛跑员,裁判发令枪响,比赛开始,直到最后一个选手跑完比赛才算结束;这里的接力棒就像是这里的信号量semaphore.acquire()和semaphore.release(),只有线程获取到信号量才可以开跑,当线程释放了信号量下一个等待线程才可以获取,只有直到最后一个线程执行完毕后,主线程才可以继续执行;
下面通过具体代码演示:
下面代码中定义的一个抽象类,实现了CountDownLatch和Semaphore的组合使用;
package com.zl.concurrency.example.temp; import com.zl.concurrency.example.ThreadPoolResource.ThreadPoolInstance; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; /** * CountDownLunch公用模板 * * @author zhagnlei * @ProjectName: MapDemo * @create 2018-09-14 16:59 * @Version: 1.0 * <p>Copyright: Copyright (zl) 2018</p> **/ @Slf4j public abstract class AbstractCountDownLunchComm { protected int clientTotal ; protected int threadTotal ; private static final ExecutorService executorService = ThreadPoolInstance.getThreadPollInstance(5, 200, 0L); public void execute(int clientTotal, int threadTotal) throws Exception { this.clientTotal = clientTotal; this.threadTotal = threadTotal; final Semaphore semaphore = new Semaphore(threadTotal); final CountDownLatch countDownLatch = new CountDownLatch(clientTotal); for (int i = 0; i < clientTotal ; i ++ ) { executorService.execute(()->{ try { semaphore.acquire(); definedMethods(); semaphore.release(); } catch (InterruptedException e) { log.error("exception"+e); } log.info("单执行完毕{}",Thread.currentThread().getId()); countDownLatch.countDown(); }); } countDownLatch.await(); executorService.shutdown(); log.info("结束:{}","执行完毕!"); } /** * @Description 自定义方法 * @return void * @throws InterruptedException * @Author zhanglei * @Date 15:42 2018/9/16 * @Param [] **/ public abstract void definedMethods() throws InterruptedException; }
测试代码:
package com.zl.concurrency.example.atomic;
import com.zl.concurrency.annoations.ThreadSafe;
import com.zl.concurrency.example.temp.AbstractCountDownLunchComm;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.atomic.AtomicInteger;
/**
* java类简单作用描述
*
* @ProjectName: MapDemo
* @Package: com.zl.concurrency.code
* @ClassName: ${TYPE_NAME}
* @Description: java类作用描述
* @Author: zhanglie
* @CreateDate: 2018/9/11 21:35
* @UpdateUser: Zhanglei
* @UpdateDate: 2018/9/11 21:35
* @UpdateRemark: The modified content
* @Version: 1.0
* <p>Copyright: Copyright (c) 2018</p>
*/
@Slf4j
@ThreadSafe
public class AtomicIntegerExample extends AbstractCountDownLunchComm {
/** 请求总数 **/
public static int clientTotal = 10;
/** 同时并发执行的线程数 **/
public static int threadTotal = 2
public static AtomicInteger count = new AtomicInteger(0);
public static void main(String[] args) throws Exception {
try {
new AtomicIntegerExample().execute(clientTotal,threadTotal);
log.info("输出:{}",count);
} catch (Exception e) {
e.printStackTrace();
}
log.info("count:{}",count.get());
}
@Override
public void definedMethods() {
count.incrementAndGet();
Thread.sleep(2000);
}
}
这里定义了10个请求总和,并发执行是2个,通过此方法来实现一个AtomicInteger的自增;即同时发送10个请求,分2个一批执行,每个执行都是对一个静态变量加1操作;
结果:
可以看到,先是每个线程执行,执行后睡2s,当所有线程执行完毕后main线程输入总和;由于设置的并发是2,从时间上可以看出是2个2个同时输出;countDownLatch.countDown()操作相当于是在一个基数减一操作,countDownLatch.await()相当于阻塞主线程,当减到0后唤醒主线程;
我们曾如此渴望生命的波澜,到后来才发现,人生最曼妙的风景是内心的淡定与从容