(1024程序员节快乐,并发编程学习内容来源于douyin cat老师)
什么是线程池
白话一点理解就是一个池子,里面存放着已经创建好的线程,当有任务提交到线程池里面来的 时候,池子中的某个线程就会主动去执行该任务,当提交过来的任务比较多,池子中的线程数 不够用时,需要能自动扩充新的线程到池子中,当然也不能无限地扩充线程数量,能进行统一 的配置,能配置最大线程个数,而当任务比较少时,池子中的线程个数就自动回收,把线程资 源释放掉;并且通常情况下,为了能缓存提交过来的未被处理的任务,就需要有一个任务队列 来存放,这就是一个线程池,你甚至可以自己来实现一个线程池;
为什么要有线程池
我们知道创建和销毁一个对象是很费时间的,特别是一些比较耗费资源的对象的创建和销毁, 比如创建数据库连接,创建网络连接,创建线程等,所以就出现“池化技术”,即复用已经创 建的对象,那么这样做能够带来 3 个好处:
-
降低资源消耗,通过复用已经创建的线程降低线程创建和销毁造成的系统资源消耗;
-
提高性能,当执行大量异步任务时线程池能够提供更好的性能,在不使用线程池时,每 当需要执行异步任务时直接 new 一个线程来运行,而线程的创建和销毁是需要开销的,而线 程池里面的线程是可复用的,不需要每次执行异步任务时都重新创建和销毁线程,直接执行任 务即可;
-
方便线程管理,线程是不能随随便滥用的,当不停地创建线程可能导致系统资源消耗殆 尽而崩溃,使用线程池可以限制创建的线程个数、动态新增线程数量等,提高了线程的可管理 性;
线程池的使用案例代码
线程池配置类
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 线程池配置
* 根据业务不同(有的业务执行快,有的业务执行慢),使用不同的线程池
* 自定义线程工厂
* 自定义拒绝策略,暂时用"调用者运行策略"
*/
@Configuration
public class ThreadPoolConfig {
/**
* 核心线程数
*/
private static final int corePoolSize = 8;
/**
* 线程池中允许的最大线程数
*/
private static final int maximumPoolSize = 16;
/**
* 线程空闲超时时间(秒)
*/
private static final long keepAliveTime = 30;
/**
* 任务队列
*/
private static final int capacity = 100;
/**
* 站内信、短信-线程池
*
* @return
*/
@Bean(name = "msgThreadPoolExecutor")
public ThreadPoolExecutor msgThreadPoolExecutor() {
return new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(capacity),
new CustomThreadFactory("msg"),
new ThreadPoolExecutor.CallerRunsPolicy());
}
}
自定义线程池工厂
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 自定义线程工厂
*/
public class CustomThreadFactory implements ThreadFactory {
private static final AtomicInteger poolNumber = new AtomicInteger(1);
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
CustomThreadFactory(String functionFlag) {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
//log4j日志输出的线程名称没有变
namePrefix = functionFlag + "-pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon()) {
t.setDaemon(false);
}
if (t.getPriority() != Thread.NORM_PRIORITY) {
t.setPriority(Thread.NORM_PRIORITY);
}
return t;
}
}
线程调用执行
这两行代码用于子线程获取父线程的headers信息,比如access_token,用于openfeign调用。
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
RequestContextHolder.setRequestAttributes(requestAttributes);
import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import javax.annotation.Resource;
import java.text.MessageFormat;
import java.util.Date;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 站内信发送工具类
*/
@Service
public class MsgZnxUtil {
private static final Logger logger = LoggerFactory.getLogger(MsgZnxUtil.class);
@Resource
private ThreadPoolExecutor msgThreadPoolExecutor;
/**
* 发送站内信
*
* @param action
*/
public void sendZnx(String action) {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
try {
msgThreadPoolExecutor.execute(() -> {
RequestContextHolder.setRequestAttributes(requestAttributes);
//code
});
} catch (Exception e) {
logger.error(String.format("%s,站内信发送失败", action), e);
}
}
}