Java多线程高并发知识点线程池和CountDownLatch

2022-06-15 09:28:40

线程池

池化是在为了复用常用的一种技术解决思路,比如连接池、线程池等,线程池主要是为了降低线程创建和销毁造成的资源消耗,进而起到解决系统资源,提高系统相应速度的目的。Java中的ThreadPoolExecutor是JDK为我们提供好的线程池工具。

ThreadPoolExecutor executor =new ThreadPoolExecutor(50,//核心池大小5000,//最大池大小200,//线程最大空闲时间,超过此空闲时间可以被收回
                TimeUnit.MILLISECONDS,//最大空闲时间的单位new ArrayBlockingQueue<Runnable>(10)//用于保存执行任务的队列,10的意思是可以允许10个任务在排队,如果队列满了,则创建新的线程。
        );

向线程池中提交任务

Task myTask =new Task();
executor.execute(myTask);//任务定义classTaskimplementsRunnable {
.....
}

executor.submit(myTask)也可以用于提交任务,但是主要用于带返回值的Task,用的并不多,因此不做赘述。

在此篇博文中我们用CountDownLatch来记录任务的完成情况,完成线程间的通信。

CountDownLatch

使用了多线程处理任务后,最常见的需要就是主线程并不知道任务的完成情况,因此无法决定是继续往下进行还是继续完毕。CountDownLatch是JDK提供给我们的多线程间通信的一个工具,用于让主线程知道任务完成的进度。

举个例子来说:

目前一名老师接到了一个任务,说要把教室里的桌子都擦干净

流程是:

  1. 把桌子擦干净
  2. 等全部桌子干净了之后,去敲上课铃。

如果不用多线程做的话,教室里有1000张桌子,老师(主线程)要逐一把桌子擦干净,然后去敲上课铃。效率比较低,于是老师叫来了50个同学(50个线程),告诉他们要擦桌子。但是问题是老师无法检查每张桌子是否都擦了,所以不知道什么时候去敲上课铃,于是约定同学擦完一张桌子就要举手报告老师一次,老师只需要记录同学的举手次数,等到同学的举手次数到了1000次,老师去敲上课铃。而这个CountDownLatch其实就是这个计数器

主线程的代码:

//声明,1000的意思,意味着1000个任务等待完成
CountDownLatch n =new CountDownLatch(1000);//将1000个任务交给线程池去处理for(int i=0;i<1000;i++){//启动一个任务
    Task myTask =new Task(i,n);
    executor.execute(myTask);
}//等待所有线程完毕try {
    n.await();
}catch (InterruptedException e) {
logger.error(e);
}//去敲上课铃

任务的代码:

publicvoidrun() {//做你的事情,擦桌子//....//事情干完了
    n.countDown();
}

当然,除了CountDownLatch,多线程通信中还存在CyclicBarrier和Semaphore等,但是CountDownLatch是其中最常用也最简单的一种方式,其他的方式,大家自行搜索理解即可。

高并发示例代码,

最后附上我经常用的做简单的多线程测试时,模拟高并发时的代码,希望能节约大家一点时间。

Tester.java

package com.test;import org.apache.log4j.LogManager;import org.apache.log4j.Logger;import java.util.concurrent.*;publicclassTester {privatestatic Logger logger = LogManager.getLogger(Tester.class);publicvoiddoSome(int num){//启用线程池(最大不能超过500个)
        ThreadPoolExecutor executor =new ThreadPoolExecutor(50,//核心池大小5000,//最大池大小200,//线程最大空闲时间,超过此空闲时间可以被收回
                TimeUnit.MILLISECONDS,//最大空闲时间的单位new ArrayBlockingQueue<Runnable>(10)//用于保存执行任务的队列,可以选择其他不同的队列来做任务管理
        );



        CountDownLatch n =new CountDownLatch(num);for(int i=0;i<num;i++){//启动一个任务
            Task myTask =new Task(i,n);
            executor.execute(myTask);
        }

        logger.info("全部执行的任务数量:"+executor.getTaskCount());
        logger.info("已完成的任务数量:"+executor.getCompletedTaskCount());
        logger.info("曾经创建过最大的线程数量:"+executor.getPoolSize());
        logger.info("活动的线程数量:"+executor.getPoolSize());//等待所有线程完毕try {
            n.await();
        }catch (InterruptedException e) {
            logger.error(e);
        }//关闭线程池
        executor.shutdown();

    }

    class Task implements Runnable {privateint taskNum;
        CountDownLatch n;publicTask(int num,CountDownLatch n) {this.taskNum = num;this.n= n;
        }publicvoidrun() {try{//TODO 做你的事情,擦桌子

            }catch(Exception e){
                logger.error(e);
                logger.info("task "+taskNum+"执行失败");
            }//事情干完了
            n.countDown();
        }
    }
}
  • 作者:frog4
  • 原文链接:https://blog.csdn.net/frog4/article/details/79850928
    更新时间:2022-06-15 09:28:40