几年前,有一个同事,针对程序打印快递单总是漏打的问题,写了一个线程池,并在开会的时候吹嘘了半天,坐着的同事没有一个吭声,一个儿一个儿闷葫芦一样坐在那里;当时,心里挺纳闷,你是这个公司的元老级人物,呆在公司怎么也有五六年了,这样的问题年年发生,怎么今天就感觉特别牛逼呢?
在同一家公司,在用户量没有暴增的情况下,一个问题连续出现两次不能解决,都应该反思本职工作是不是做到位了。本是分内的事,怎么好意思拿出来炫耀呢,况且那个任务又没有交给其他同事处理,怎么见得别人解决不了呢?
当然,上面这些都是废话,说了这么多废话,无非是想说说线程池的问题。写个异步的、高并发的线程池有那么难吗?
办法总在问题后,遇到问题了,才会千方百计地去找解决的方法。不怕找不到方法,就怕花在上面的时间不够、姿势不对。
很巧,前几天一位同事在项目里写了个定时任务,无意中发现他还加了这么个配置文件,下面这个方法,怎么越看越眼熟呢,这个不是线程池吗,定时任务中也没看到用到它呀?!
@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
网友评论