# 实际开发中线程池的使用,批量插入数据
线程资源必须通过线程池提供,不允许在应用中自行显示的创建线程,线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
说明:Executor返回的线程池对象的弊端如下:
(1)FixedThreadPool 和 SingleThreadPool:允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
(2)CachedThreadPool 和 ScheduledThreadPool:允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
以上是阿里巴巴Java开发手册中的规约。所以下面我用ThreadPoolExecutor的方式,来创建线程池,实际案例:向数据库中批量插入数据。
**方式一:在方法中手动创建线程池**
/**
* 使用线程池来插入数据
* @param users 我这里设计了10000条数据测试
* @return
*/
public String addAllUsers(List<MyUser> users){
String sql = "insert into myuser(id,user_name,age,address,sex) values(?,?,?,?,?)";
ThreadPoolExecutor threadPoolExecutor =
new ThreadPoolExecutor(5,10,10,
TimeUnit.MINUTES,new LinkedBlockingDeque<>(),new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < users.size(); i++) {
MyUser myUser = users.get(i);
threadPoolExecutor.execute(()->{
try {
String name = threadPoolExecutor.getClass().getName();
log.info("线程"+name+"在工作");
String id = UUID.randomUUID().toString();
jdbcTemplate.update(sql,id,myUser.getName(),myUser.getAge(),myUser.getAddress(), myUser.getSex());
}catch (Exception e){
e.printStackTrace();
}
});
}
//终止线程池
threadPoolExecutor.shutdown();
return "success";
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/d015bacb225b464087546cfbc329500e.png#pic_center)
**方式二:项目中常用,需要用创建一个公共组件供大家调用**
1.创建线程池配置类
package com.example.demo.common;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.*;
/**
* @Date:2023/3/5-15:54
* @Description:连接池配置类
* @version:1.0
*/
@Configuration
public class ThreadPoolConfig {
//核心线程数
private volatile int corePoolSize = 10;
//最大线程数
private volatile int maximumPoolSize = 30;
//空闲线程最大存活时间
private volatile int keepAliveTime = 100;
//阻塞队列
private static final BlockingDeque defaultBlockDeque = new LinkedBlockingDeque(100000);
//默认的拒绝策略
private static final RejectedExecutionHandler defaultHandler =
new ThreadPoolExecutor.AbortPolicy();
@Bean(name = "ScmThreadPoolExecutor")
public ThreadPoolExecutor getThreadPoolTaskExecutor(){
ThreadPoolExecutor threadPoolExecutor =
new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,
TimeUnit.SECONDS,defaultBlockDeque,defaultHandler);
return threadPoolExecutor;
}
}
2.从Spring容器中获取bean
package com.example.demo.common;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* @Date:2023/3/5-16:15
* @Description:从Spring容器中获取Bean
* @version:1.0
*/
@Component
public class SpringContextHelper implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContextHelper.applicationContext = applicationContext;
}
//根据类获取bean
public static Object getBean(Class<?> clazz) throws BeansException{
return applicationContext.getBean(clazz);
}
//根据名称获取bean
public static Object getBean(String name) throws BeansException{
return applicationContext.getBean(name);
}
}
3.通过反射执行方法的工具类
package com.example.demo.common;
import lombok.extern.log4j.Log4j2;
import java.lang.reflect.Method;
/**
* @Date:2023/3/5-16:20
* @Description:通过反射执行方法的工具类
* @version:1.0
*/
@Log4j2
public class ReflectionUtil {
/**
*
* @param object 服务对象
* @param methodName 方法名称
* @param args 参数
* @throws Exception
*/
public static void invokeMethod(Object object, String methodName, Object[] args) throws Exception{
log.debug("invokeMethod start : 服务对象={},调用方法={}",new Object[]{object,methodName} );
Class<?>[] paramClasses = null;
if (args.length > 0){
paramClasses = new Class<?>[args.length];
for (int i = 0; i < args.length; i++) {
paramClasses[i] = args[i].getClass();
}
}
Method method = object.getClass().getMethod(methodName, paramClasses);
method.setAccessible(true);
method.invoke(object,args);
log.debug("invokeMethod end ");
}
}
4.异步任务类
package com.example.demo.common;
/**
* @Date:2023/3/5-16:40
* @Description:异步任务类
* @version:1.0
*/
public class AsyncTask implements Runnable{
//服务对象
private Object object;
//服务方法
private String methodName;
//方法参数
private Object[] args;
public AsyncTask(Object object,String methodName,Object[] args){
this.object = object;
this.methodName = methodName;
this.args = args;
}
@Override
public void run() {
try {
//这里就用到了反射来进行执行具体的方法
ReflectionUtil.invokeMethod(object,methodName,args);
}catch (Exception e){
e.printStackTrace();
}
}
}
5.线程池使用工具类
package com.example.demo.common;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Date:2023/3/5-16:44
* @Description:com.example.demo.common
* @version:1.0
*/
public class AsyncTaskUtil {
private volatile static ThreadPoolExecutor executor;
private static final ReentrantLock LOCK = new ReentrantLock();
/**
* 获取线程池
* @return
*/
private static ThreadPoolExecutor getThreadPoolExecutor(){
if (executor == null){
LOCK.lock();
try {
if (executor == null){
executor = (ThreadPoolExecutor)SpringContextHelper.getBean("ScmThreadPoolExecutor");
}
}catch (Exception e){
e.printStackTrace();
}finally {
LOCK.unlock();
}
}
return executor;
}
public static void asyncTask(Object object,String methodName,Object[] args){
AsyncTask asyncTask = new AsyncTask(object, methodName, args);
asyncTask(asyncTask);
}
public static void asyncTask(Runnable asyncTask){
AsyncTaskUtil.getThreadPoolExecutor().execute(asyncTask);
}
public static <T> Future<T> asyncTask(Callable<T> callableTask){
return AsyncTaskUtil.getThreadPoolExecutor().submit(callableTask);
}
}
6.在业务中真正的开始使用上面的基础工具类
(1)调用方式一:将要异步执行的代码单独写一个类,并且这个类继承Runnable接口
创建一个类
package com.example.demo.service;
import com.example.demo.dto.MyUser;
import lombok.extern.log4j.Log4j2;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import java.util.UUID;
/**
* @Date:2023/3/5-20:09
* @Description:com.example.demo.service
* @version:1.0
*/
@Log4j2
@Service
public class AddUsers implements Runnable{
private MyUser myUser;
private JdbcTemplate jdbcTemplate;
public AddUsers(MyUser myUser, JdbcTemplate jdbcTemplate){
this.myUser = myUser;
this.jdbcTemplate = jdbcTemplate;
}
public AddUsers(){}
@Override
public void run() {
try {
//具体插入数据库要执行的方法代码
insertUser(myUser);
}catch (Exception e){
e.printStackTrace();
}finally {
}
}
/**
* 插入一条数据
* @param user
*/
public void insertUser(MyUser user){
String sql = "insert into myuser(id,user_name,age,address,sex) values(?,?,?,?,?)";
String id = UUID.randomUUID().toString();
jdbcTemplate.update(sql,id,user.getName(),user.getAge(),user.getAddress(), user.getSex());
}
}
具体service层进行调用:
public String addUsers(List<MyUser> users){
log.info("数据插入--start");
for (int i = 0; i < users.size(); i++) {
try {
AsyncTaskUtil.asyncTask(new AddUsers(users.get(i),jdbcTemplate));
}catch (Exception e){
e.printStackTrace();
}
}
log.info("数据插入结束");
return "seccess";
}
(1)调用方式二:使用反射机制来调用
public String addAllUsersOthersMethod(List<MyUser> users){
log.info("数据插入--start");
//从容器中获取到当前类的bean对象,不仅获取了当前类的bean,并且将自动装配的其他类也加载上。
//直接new UserService 的话,那么在你下面需要调用的insertUser就无法使用你注入的类。
UserService userService = (UserService)SpringContextHelper.getBean(UserService.class);
for (int i = 0; i < users.size(); i++) {
try {
//具体参数为:类,方法,方法参数
AsyncTaskUtil.asyncTask(userService,"insertUser",new MyUser[]{users.get(i)});
}catch (Exception e){
e.printStackTrace();
}
}
log.info("数据插入结束");
return "success";
}
/**
* 插入一条数据
* @param myUser
*/
public void insertUser(MyUser myUser){
String sql = "insert into myuser(id,user_name,age,address,sex) values(?,?,?,?,?)";
String id = UUID.randomUUID().toString();
jdbcTemplate.update(sql,id,myUser.getName(),myUser.getAge(),myUser.getAddress(), myUser.getSex());
}
以上三种方式均可以将大数据量异步的插入到数据库中。