美文网首页
SpringBoot中的线程池,来个小Demo走一波

SpringBoot中的线程池,来个小Demo走一波

作者: 程就人生 | 来源:发表于2020-04-29 22:01 被阅读0次

    几年前,有一个同事,针对程序打印快递单总是漏打的问题,写了一个线程池,并在开会的时候吹嘘了半天,坐着的同事没有一个吭声,一个儿一个儿闷葫芦一样坐在那里;当时,心里挺纳闷,你是这个公司的元老级人物,呆在公司怎么也有五六年了,这样的问题年年发生,怎么今天就感觉特别牛逼呢?

    在同一家公司,在用户量没有暴增的情况下,一个问题连续出现两次不能解决,都应该反思本职工作是不是做到位了。本是分内的事,怎么好意思拿出来炫耀呢,况且那个任务又没有交给其他同事处理,怎么见得别人解决不了呢?

    当然,上面这些都是废话,说了这么多废话,无非是想说说线程池的问题。写个异步的、高并发的线程池有那么难吗?

    办法总在问题后,遇到问题了,才会千方百计地去找解决的方法。不怕找不到方法,就怕花在上面的时间不够、姿势不对。

    很巧,前几天一位同事在项目里写了个定时任务,无意中发现他还加了这么个配置文件,下面这个方法,怎么越看越眼熟呢,这个不是线程池吗,定时任务中也没看到用到它呀?!

    @Configuration
    @EnableAsync
    public class AsyncConfig {
    
        @Bean
        public Executor taskExecutor() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setCorePoolSize(5);   //核心线程数
            executor.setMaxPoolSize(40);  // 队列大小
            executor.setQueueCapacity(12); // 最大线程数
            executor.initialize();
            return executor;
        }
    }
    
    

    无意中翻到了源码里相似的一段:


    又想起,买过的一本书里有一个线程池的案例,这个例子可是原生态的,继承Thread实现的:

    import java.util.concurrent.ConcurrentLinkedQueue;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     * 建立线程池,将需要执行的任务加入到线程池中
     * @author 程就人生
     * @date 2020年4月29日
     */
    public class FutureTaskScheduler extends Thread {
        
        private static Logger log = LoggerFactory.getLogger(FutureTaskScheduler.class);
        
        //并发的任务队列,将要执行的线程都加入到这个队列里
        private ConcurrentLinkedQueue<Runnable> executeTaskQueue = new ConcurrentLinkedQueue<Runnable>();
        
        //线程休眠时间
        private long sleepTime = 200;
        
        //创建一个线程池,活跃线程数最多10个,再有加入到队列里的,只能等待
        private ExecutorService pool = Executors.newFixedThreadPool(10);
    
        private static FutureTaskScheduler inst = new FutureTaskScheduler();
    
        private FutureTaskScheduler() {
            this.start();
        }
    
        /**
         * 添加任务
         * @param executeTask
         */
        public static void add(Runnable executeTask) {
            inst.executeTaskQueue.add(executeTask);
        }
    
        @Override
        public void run() {
            while (true) {
                handleTask();// 处理任务
                threadSleep(sleepTime);
            }
        }
    
        private void threadSleep(long time) {
            try {
                sleep(time);
            } catch (InterruptedException e) {
                log.error("线程等待异常:", e);
            }
        }
    
        /**
         * 处理任务队列,检查其中是否有任务
         */
        private void handleTask() {
            Runnable executeTask;
            //任务队列里有任务时
            while (executeTaskQueue.peek() != null) {
                //取出任务
                executeTask = executeTaskQueue.poll();
                handleTask(executeTask);
            }
        }
    
        /**
         * 执行任务操作
         *
         * @param executeTask
         */
        private void handleTask(Runnable executeTask) {
            pool.execute(new ExecuteRunnable(executeTask));
        }
    
        /**
         * 
         * @author 程就人生
         * @date 2020年4月29日
         */
        class ExecuteRunnable implements java.lang.Runnable {
            
            Runnable executeTask;
    
            ExecuteRunnable(Runnable executeTask) {
                this.executeTask = executeTask;
            }
    
            public void run() {
                executeTask.run();
            }
        }
    }
    

    这段代码不难懂,看一看就明白了;这个线程池的使用,也很简单,只需要往里面添加任务就可以了;

    FutureTaskScheduler.add(() -> {
                    //要执行任务的添加
                        //TODO
                });
    

    现在,大伙都在使用SpringBoot了,第一段代码是不是在SpringBoot里面实现的线程池呢?答案,是的,只是那个是配置;下面请看完整的代码。

    首先,线程池配置文件;

    import java.util.concurrent.Executor;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.scheduling.annotation.EnableAsync;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
     
    /**
     * 线程池配置文件
     * @author 程就人生
     * @date 2020年4月29日
     */
    @Configuration
    @EnableAsync
    public class MThreadConfig  {
    
        @Bean("getExecutor")
        public Executor getExecutor() {
    
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
    
            //设置核心线程数
            executor.setCorePoolSize(5);
    
            //设置最大线程数
            executor.setMaxPoolSize(40);
    
            //队列容量
            executor.setQueueCapacity(12);
            
            //设置线程名
            executor.setThreadNamePrefix("程就人生线程之--");
    
            //设置多余线程等待的时间,单位:秒,Default is 60
            executor.setKeepAliveSeconds(10);
    
            // 初始化线程
            executor.initialize();
           
            // 设置拒绝策略
            //executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
            // 等待所有任务结束后再关闭线程池
            //executor.setWaitForTasksToCompleteOnShutdown(true);
    
            return executor;
    
        }
    }
    

    第二步,定义几个可执行的任务,注意注解上的Value;

    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.concurrent.Future;
    
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.scheduling.annotation.AsyncResult;
    import org.springframework.stereotype.Component;
    
    /**
     * 线程服务类
     * @author 程就人生
     * @date 2020年4月29日
     */
    @Component
    public class ThreadService {
         
         @Async("getExecutor")
         public void test(int i){
             
            SimpleDateFormat format=new SimpleDateFormat("HH:mm:ss");
            try {
                Thread.sleep(10000);
                System.out.println("多线程异步执行:" +i + " " + Thread.currentThread().getName() + "  "+format.format(new Date()));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
         }
         
         /**
          * 有返回值的多线程
          * @param req
          * @return
          *
          */
         @Async("getExecutor")
         public Future<String> execute(String req) {
            SimpleDateFormat format=new SimpleDateFormat("HH:mm:ss");
            try {
                Thread.sleep(10000);
                System.out.println("多线程异步执行:"+Thread.currentThread().getName()+ req + "  "+format.format(new Date()));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return new AsyncResult<>(req);
        }
    }
    
    

    第三步,调用任务,加入线程池;

    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.Future;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import com.example.demo.thread.ThreadService;
    
    /**
     * 多线程测试代码
     * @author 程就人生
     * @date 2020年4月29日
     */
    @RestController
    public class IndexController {
    
        @Autowired
        private ThreadService threadService;
        
        @GetMapping("/index")
        public void index(){
            for(int i=0;i<10;i++){
                threadService.test(i);
            }
        }
        
        
        @GetMapping("/index1")
        public String index1(HttpServletRequest req){
            String str = null;
            String url = req.getParameter("url") == null ? "" : req.getParameter("url");
            Future<String> future = threadService.execute(url);
            try {
                str = future.get();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
            return str;
        }
    }
    

    最后,启动项目,看执行的结果;

    又参考了其他网友们的写法,写法各种各样,类还是那几个类,就看怎么灵活使用了,更看在什么业务场景适用了。

    线程池四个基本组成部分

    • 线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;
    • 工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
    • 任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
    • 任务队列(taskQueue):用于存放没有处理的任务,提供一种缓冲机制。

    当然,写到这里,也只是熟悉一下线程池的使用,唯有在业务场景中反复打磨代码,才能写出好的Demo来,上面的也只是参考其他网友的战果。

    参考文档:
    https://blog.csdn.net/weixin_42749765/article/details/93498685
    https://blog.csdn.net/xm526489770/article/details/83510454
    https://blog.csdn.net/qa76774730/article/details/83057699
    https://blog.csdn.net/m0_37606574/article/details/87805473

    相关文章

      网友评论

          本文标题:SpringBoot中的线程池,来个小Demo走一波

          本文链接:https://www.haomeiwen.com/subject/rennwhtx.html