日常记录——多线程与高并发—Executors提供的常用线程池、基于ThreadPoolExecutor自定义线程池

一、简介
Executors工具类提供了四种线程池的创建分别为:newSingleThreadExecutor(单线程池)、newFixedThreadPool(固定线程池)、newCachedThreadPool(缓存线程池)、newScheduledThreadPool(周期线程池),四种线程池各有特点,分别介绍下:
1.newSingleThreadExecutor:创建一个单线程的线程池,这个线程池只有一个线程在工作,线程池保证所有任务的执行顺序按照任务的提交顺序执行。

public static void main(String[] args) {
        ExecutorService executor1 = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            final int j = i+1;
            executor1.execute(() -> {
                System.out.println(j+"  "+Thread.currentThread().getName());
            });
        }
        executor1.shutdown();
    }

newSingleThreadExecutor方法:可见核心线程为1,最大线程数为1,用的容量为Integer.MAX_VALUE的队列(如果任务堆积,有OOM风险)。

	public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

2.newFixedThreadPool: 创建一个固定长度线程池,可控制线程最大并发数,超出的线程会在队列中等待。

	public static void main(String[] args) {
        ExecutorService executor3 = Executors.newFixedThreadPool(2);
		 for (int i = 0; i < 10; i++) {
	           final int j = i+1;
	            executor3.execute(() -> {
	                System.out.println(j+"  "+Thread.currentThread().getName());
	            });
	        }
       	executor3.shutdown();
    }

newFixedThreadPool方法:核心线程数与最大线程数想等,保证最大并发量,用的容量为Integer.MAX_VALUE的队列(如果任务堆积,有OOM风险)。

	public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

3.newCachedThreadPool:创建一个可缓存线程池,如果有空闲线程,超过存活时间回收,如果没空闲处理任务,则新建线程。

 public static void main(String[] args) {
        ExecutorService executor2 = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            final int j = i+1;
            executor2.execute(() -> {
                System.out.println(j+"  "+Thread.currentThread().getName() +"  " +System.currentTimeMillis());
            });
        }
        executor2.shutdown();
    }

newCachedThreadPool方法:核心线程数为0,最大线程数为Integer.MAX_VALUE(任务堆积,并且每个任务执行时间过长,有OOM风险),空闲线程存活时间为60S,使用容量为0的传递队列,即来一个任务就交给线程处理。

	public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

4.newScheduledThreadPool:创建一个定长线程池,支持定时及周期性任务执行。

    public static void main(String[] args) {
        ScheduledExecutorService executor4 = Executors.newScheduledThreadPool(2);
        for (int i = 0; i < 3; i++) {
            final int j = i+1;
            //延时j秒后执行
            executor4.schedule(() ->{
                System.out.println(j+"  "+Thread.currentThread().getName() +"  " +System.currentTimeMillis());
            },j,TimeUnit.SECONDS);
            //延时 + 定时  首次执行延迟2秒  之后每次与上次执行间隔两秒 
            executor4.scheduleAtFixedRate(() -> {
                System.out.println("第  " + j +"个任务开始执行" +"  "+ System.currentTimeMillis());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("第  " + j +"个任务开始执行结束");
//                System.out.println(j+"  "+Thread.currentThread().getName() +"  " +System.currentTimeMillis());
            },2,2,TimeUnit.SECONDS);
        }
        // 延时 + 定时 把shutdown 注释掉看效果
        executor4.shutdown();
    }

newScheduledThreadPool方法:最终调用的ThreadPoolExecutor的构造方法,核心线程数为corePoolSize,最大线程数为Integer.MAX_VALUE(任务堆积,创建大量线程,有OOM风险),基于DelayedWorkQueue优先级队列实现延时和定时功能。

 	public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

二、基于ThreadPoolExecutor自定义线程池

通过上文可发现在任务量堆积过多的时候,上述四种线程池会有OOM风险,并且默认的拒绝策略都是AbortPolicy,实际运行的要执行的任务,不应该直接不管(当然线程池设置合理,很难遇到这种情况),为了规避这两点问题,尝试基于ThreadPoolExecutor自定义一个线程池,但核心线程池的数量该如何设置才能最好的利用CPU性能呢,《Java Concurrency in Practice》即《java并发编程实践》书中给出公式为:
threads=Ncpu * Ucpu * (1+w/c)
Ncpu=CPU核心数
Ucpu=CPU使用率,0~1
W/C=等待时间与计算时间的比率
IO密集型:一般情况下,如果存在IO,那么肯定w/c>1(阻塞耗时一般都是计算耗时的很多倍),保守点取1即,Ucpu肯定想全利用,threads=Ncpu*(1+1)=2Ncpu。
计算密集型:假设没有等待w=0,则W/C=0. threads=Ncpu。
Ncpu可通过Runtime.getRuntime().availableProcessors()获取。
目前开发多为WEB应用,包含网络传输、数据库、存间的交互都涉及到IO。所以核心线程数量可以定义为Runtime.getRuntime().availableProcessors()*2
代码如下:

package com.company.threadpool;

import java.util.concurrent.*;

public class DIYThreadPool {
	//防止实例化
    private DIYThreadPool(){};
    //核心线程数
    private static final int CORE_POOL_SIZE = Runtime.getRuntime().availableProcessors()*2;
    //最大线程数
    private static final int MAXIMUM_POOL_SIZE = Runtime.getRuntime().availableProcessors()*4;
    //存活时间
    private static final long KEEP_ALIVE_Time = 60L;
    private static final TimeUnit UNIT = TimeUnit.SECONDS;
    //默认队列大小
    private static final int TASK_SIZE = 500;
    //阻塞队列
    private static BlockingQueue<Runnable> taskQueue = new ArrayBlockingQueue<>(TASK_SIZE);
    //线程池名字
    private static final String THREAD_POOL_NAME = "DIYThreadPool";
    //单例  静态内部类
    private static class Instance {
        private final static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE,MAXIMUM_POOL_SIZE,KEEP_ALIVE_Time,UNIT,taskQueue,new MyThreadFactory(),new MyRejectedExecutionHandler());
    }

    public static ThreadPoolExecutor getInstance() {
        return Instance.threadPool;
    }
    /**
     * 自定义线程工厂类
     */
    public static class MyThreadFactory implements ThreadFactory {

        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r,"");
            thread.setName(THREAD_POOL_NAME + "-"+thread.getId());
            //设定非守护线程
            if (thread.isDaemon())
                thread.setDaemon(false);
            //去掉优先级
            if (thread.getPriority() != Thread.NORM_PRIORITY)
                thread.setPriority(Thread.NORM_PRIORITY);
            return thread;
        }
    }
    /**
     * 自定义拒绝策略
     */
    public static class MyRejectedExecutionHandler implements RejectedExecutionHandler{

        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            //1.记录日志  存到 redis  有空闲时间取出来执行   
            //2.重试  直到放进去
            //3.重试N次后  存日志  redis 有空闲时间取出来执行
            //以上根据业务实现
            System.out.println("线程池:"+executor +"拒绝执行任务...:"+r);
        }

    }

}


猜你喜欢

转载自blog.csdn.net/weixin_43001336/article/details/107349889