美文网首页
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