多线程并发同步业务场景与解决方案
1)semaphore 信号量 (控制并发数)
业务场景1:假如现在有10个人去同一家公司面试,但是只有3个面试官,那么同一时间只有2个人面试,当3个人中的任意一个面试结束之后,等待的7个人又会有一个人可以去面试。
需求分析:人数=线程数面试官=资源正在面试=线程正在执行
面试结束=线程执行结束等待面试人数=线程阻塞
解决方案:信号量 semaphore
代码Demo:
public class SemaphoreTest implements Runnable {
private int num;
private Semaphore semaphore;
public SemaphoreTest(int num,Semaphore semaphore){
this.num=num;
this.semaphore=semaphore;
}
public void run() {
try {
semaphore.acquire();
//获取信号量许可,才能进入
System.out.println("面试者"+num+"进入房间……");
Thread.sleep((long)Math.random()*10000);
System.out.println("面试者"+num+"交谈中……");
Thread.sleep((long)Math.random()*10000);
System.out.println("面试者"+num+"离开房间……");
semaphore.release();
//释放信号量许可
} catch (InterruptedException e) {
e.printStackTrace();
} }
public static void main(String[] args) {
final Semaphore s=new Semaphore(3);
//并发数为3
ExecutorService threadPool=Executors.newCachedThreadPool();
//线程池
for(int i=0;
i<10;
i++){
threadPool.execute(new SemaphoreTest((i+1),s));
}
threadPool.shutdown();
} }
2)Cyclicbarrier同步屏障(用于多线程计算数据,最后合并计算结果)
业务场景2:公司周末组织去聚餐、首先各自从家里出发到聚餐地点,当所有人全部到齐之后才开始吃饭,如果未到齐,到的人就只能等待在那里,直到所有人都到达之后,才可以一起做事。
案例代码:
public class CyclicbarrierDemo {
public static void main(String[] args) {
final CyclicBarrier cb=new CyclicBarrier(3,new Runnable() {
public void run() {
System.out.println("吃饭前,一起做的事情");
} });
ExecutorService threadPool=Executors.newCachedThreadPool();
//线程池
for(int i=0;
i<3;
i++){
final int user=i+1;
Runnable r=new Runnable() {
public void run() {
try {
Thread.sleep((long)Math.random()*10000);
System.out.println(user+"到达聚餐地点,当前已有"+(cb.getNumberWaiting()+1)+"人到达");
cb.await();
//等待,只有当线程都到达之后,才能往下走
if(user==1){ System.out.println("人员到齐");
}
Thread.sleep((long)Math.random()*10000);
System.out.println(user+"吃完饭,回家……");
//dosometing
} catch (Exception e) {
e.printStackTrace();
} } };
threadPool.execute(r);
}
threadPool.shutdown();
} }
3)Exchanger 线程之间交换数据
public class ExchangerDemo {
public static void main(String[] args) {
final Exchangerexchanger=new Exchanger();
【多线程并发同步业务场景与解决方案】 ExecutorService threadPool=Executors.newCachedThreadPool();
//线程池
threadPool.execute(new Runnable() {
public void run() { String sc="a";
try {
String js=exchanger.exchange(sc);
//js=b
} catch (InterruptedException e) { e.printStackTrace();
} } });
threadPool.execute(new Runnable() {
public void run() {
String sc="b";
try { String js=exchanger.exchange(sc);
//js=a
} catch (InterruptedException e) { e.printStackTrace();
} } });
} }
执行后,连个线程的数据进行了交换。
4)CountDownLatch 倒计时器
业务场景4:有一个任务a,他需要等待其他几个任务(BCD)都执行完毕之后才能来执行这个任务。
public static void main(String[] args) throws InterruptedException {
final CountDownLatch latch=new CountDownLatch(3);
new Thread(){
public void run() {
System.out.println("子任务B"+Thread.currentThread().getName()+"正在执行");
latch.countDown();
//倒计时减一
};
}.start();
new Thread(){
public void run() {
System.out.println("子任务C"+Thread.currentThread().getName()+"正在执行");
latch.countDown();
//倒计时减一
};
}.start();
new Thread(){
public void run() {
System.out.println("子任务D"+Thread.currentThread().getName()+"正在执行");
latch.countDown();
//倒计时减一
};
}.start();
System.out.println("等待3个任务执行完毕,"+Thread.currentThread().getName()+"组任务开始执行。");
latch.await();
System.out.println("继续执行主任务!");
}
CountDownLatch 与Cyclicbarrier 的区别:
1)共同点:都能够实现线程之间的等待。
2)不同点:
CountDownLatch :1)一般用于某个线程A等待其他若干线程执行完任务后,它才能执行。2)它是不能够重复用的。
Cyclicbarrier :1)一般用于一组线程互相等待,然后这一组线程同时执行。2)它可以重复使用。
推荐阅读
- 放屁有这三个特征的,请注意啦!这说明你的身体毒素太多
- 爱就是希望你好好活着
- 昨夜小楼听风
- 知识
- 死结。
- 我从来不做坏事
- 烦恼和幸福
- 关于QueryWrapper|关于QueryWrapper,实现MybatisPlus多表关联查询方式
- Linux下面如何查看tomcat已经使用多少线程
- 说得清,说不清