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

线程池

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

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

向线程池中提交任务

Task myTask = new Task();
executor.execute(myTask);

//任务定义
class Task implements Runnable {
.....
}

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);
}
//去敲上课铃

任务的代码:

public void run() {
    //做你的事情,擦桌子
    //....

    //事情干完了
    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.*;

public class Tester {

    private static Logger logger = LogManager.getLogger(Tester.class);


    public void doSome(int num){
        //启用线程池(最大不能超过500个)
        ThreadPoolExecutor executor = new ThreadPoolExecutor(50,//核心池大小
                5000,//最大池大小
                200,//线程最大空闲时间,超过此空闲时间可以被收回
                TimeUnit.MILLISECONDS, //最大空闲时间的单位
                new ArrayBlockingQueue(10)//用于保存执行任务的队列,可以选择其他不同的队列来做任务管理
        );



        CountDownLatch n = new CountDownLatch(num);

        for(int i=0;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 {

        private int taskNum;
        CountDownLatch n;



        public Task(int num,CountDownLatch n) {
            this.taskNum = num;
            this.n= n;
        }

        public void run() {
            try{
                //TODO 做你的事情,擦桌子

            }catch(Exception e){
                logger.error(e);
                logger.info("task "+taskNum+"执行失败");
            }

            //事情干完了
            n.countDown();
        }
    }
}

你可能感兴趣的:(Java)