线程池及Spring Boot举例

  • 创建线程池:ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, RejectedExecutionHandler handler)

    • corePoolSize:线程池的基本大小
    • maximumPoolSize:线程池最大线程大小
    • keepAliveTime:线程空闲后的存活时间
    • unit:线程空闲后的存活时间单位
    • workQueue:存放任务的阻塞队列
    • handler:当队列和最大线程池都满了之后的饱和策略
  • 线程池中定义的状态

    //运行状态,指可以接受任务执行队列里的任务
    private static final int RUNNING    = -1 << COUNT_BITS;
    //指调用了 shutdown() 方法,不再接受新任务了,但是队列里的任务得执行完毕
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    // 指调用了 shutdownNow() 方法,不再接受新任务,同时抛弃阻塞队列里的所有任务并中断所有正在执行任务
    private static final int STOP       =  1 << COUNT_BITS;
    //所有任务都执行完毕,在调用 shutdown()/shutdownNow() 中都会尝试更新为这个状态
    private static final int TIDYING    =  2 << COUNT_BITS;
    //终止状态,当执行 terminated() 后会更新为这个状态
    private static final int TERMINATED =  3 << COUNT_BITS;
    
  • 线程池的execute方法

    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
         //获取当前线程池的状态
        int c = ctl.get();
        //当前线程数量小于 coreSize 时创建一个新的线程运行
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        //如果当前线程处于运行状态,并且写入阻塞队列成功
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            //双重检查,再次获取线程池状态;如果线程池状态变了(非运行状态)就需要从阻塞队列移除任务,并尝试判断线程是否全部执行完毕。同时执行拒绝策略
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)//如果当前线程池为空就新创建一个线程并执行
                addWorker(null, false);
        }
        else if (!addWorker(command, false))//如果判断为非运行状态,尝试新建线程,如果失败则执行拒绝策略
            reject(command);
    }
    
  • 拒绝策略

    • new ThreadPoolExecutor.CallerRunsPolicy():由调用线程处理该任务
    • new ThreadPoolExecutor.AbortPolicy():丢弃任务并抛出RejectedExecutionException异常
    • new ThreadPoolExecutor.DiscardPolicy():也是丢弃任务,但是不抛出异常
    • new ThreadPoolExecutor.DiscardOldestPolicy():丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
  • 配置线程

    • IO 密集型任务:由于线程并不是一直在运行,所以可以尽可能的多配置线程,比如 CPU 个数 * 2
    • CPU 密集型任务(大量复杂的运算)应当分配较少的线程,比如 CPU 个数相当的大小
  • Spring Boot实现:

    • 线程池管理器
    @Slf4j
    @Component
    public class ThreadPoolManager {
    
        private static volatile ThreadPool threadPool;
    
        public static ThreadPool getThreadPool() {
            if (threadPool == null) {
                initThreadPool();
            }
            return threadPool;
        }
    
        public static void initThreadPool() {
            if (threadPool == null) {
                synchronized (ThreadPoolManager.class) {
                    if (threadPool == null) {
                        // 获取处理器数量
                        int cpuNum = Runtime.getRuntime().availableProcessors();
                        // 根据cpu数量,计算出合理的线程并发数
                        int threadNum = cpuNum * 2;
                        log.info("cpuNum [{}],threadNum [{}]", cpuNum,threadNum);
                        threadPool = new ThreadPool(threadNum, threadNum, 0L);
                    }
                }
            }
        }
    
        /**
         * PostConstruct修饰的方法会在服务器加载Servle的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行。方法在destroy()方法执行执行之后执行
         */
        @PreDestroy
        public void destroyThreadPool() {
            if (threadPool != null) {
                log.info("thread pool shutdown.");
                threadPool.shutdown();
            }
        }
    
        @Getter
        public static class ThreadPool {
            private ThreadPoolExecutor threadPoolExecutor;
    
            private ThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime) {
                //定义线程池名字
                ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("thread pool").build();
                //创建线程池
                /**
                 *
                 */
                threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime,
                        TimeUnit.MILLISECONDS, new LinkedBlockingDeque(), namedThreadFactory,
                        new ThreadPoolExecutor.AbortPolicy());
            }
    
            /**
             * 提交一个任务到线程池中
             *
             * @param runnable
             */
            public void execute(Runnable runnable) {
                if (runnable == null) {
                    return;
                }
                threadPoolExecutor.execute(runnable);
            }
    
            /**
             * 提交一个任务到线程池中,并返回执行结果
             *
             * @param futureTask
             */
            public  void submit(FutureTask futureTask) {
                if (futureTask == null) {
                    return;
                }
                threadPoolExecutor.submit(futureTask);
            }
    
            /**
             * 从线程队列中移除对象
             *
             * @param runnable
             */
            public void cancel(Runnable runnable) {
                if (threadPoolExecutor != null) {
                    threadPoolExecutor.getQueue().remove(runnable);
                }
                threadPoolExecutor.shutdown();
            }
    
            public  void cancel(Callable task) {
                if (threadPoolExecutor != null) {
                    threadPoolExecutor.getQueue().remove(task);
                }
            }
    
            /**
             * 不再接受新任务了,但是队列里的任务得执行完毕
             */
            public void shutdown() {
                if (threadPoolExecutor != null) {
                    threadPoolExecutor.shutdown();
                }
            }
        }
    }
    
  • 执行任务线程

    @Slf4j
    public class ConsumerQueueThread implements Runnable {
    
        private static volatile int i = 0;
    
        @Override
        public void run() {
            log.info("start thread [{}] time [{}]",i++,System.currentTimeMillis());
            log.info("queue size [{}]",ThreadPoolManager.getThreadPool().getThreadPoolExecutor().getQueue().size());
        }
    }
    
  • service调用

    @Slf4j
    @Service("threadPoolService")
    public class ThreadPoolServiceImpl implements ThreadPoolService {
    
        @Override
        public boolean testExcuteThread(int threadNumber) {
            log.info("start testExcuteThread");
            for (int i = 0;i
  • 坑:如果不是自定义线程池,需注意的是原生线程池中执行任务抛出的异常会在afterExecute进行空处理,所以我们需在runable将异常catch住,或者自定义线程池重写afterExecute方法

更多文章:
CSDN博客
简书博客
公众号:代码小搬运
代码小搬运.jpg

你可能感兴趣的:(Java开发,#,线程)