线程池在项目中的实际使用

前言

  大家为了面试肯定都学习过多线程,多线程也逐渐变成了面试重点内容(本文不讲解线程池的相关知识,只展示线程池项目整合案列),但是线程池在项目中的具体使用及整合大家可能没有接触过,网上也可能找不到合适的案列,下面我把我们之前项目中我们老大整合的线程池的案列分享出来供大家使用。

线程池

1.线程池的选择

  JDK工具中一共给我们提供了多种线程池,包含固定长度的线程池、单线程线程池、以及可扩容的线程池等。但是这些线程池在我们实际工作的项目中都不实用的,那么有的童鞋要问了,为什么JDK提供的线程池还不实用呢,是因为阿里巴巴开发手册中并发处理章节里第四点明确表示了线程池的使用规范:
线程池在项目中的实际使用_第1张图片
那么我们应该选择哪种线程池呢,其实Spring框架早就已经帮我们解决了这个难题,因此Spring框架提供了ThreadPoolTaskExecutor线程池

2.ThreadPoolTaskExecutor和ThreadPoolExecutor的区别

ThreadPoolTaskExecutor是spring core包中提供的,而ThreadPoolExecutor是JDK中的JUC包下提供的,并且ThreadPoolTaskExecutor是在ThreadPoolExecutor的基础上进一步进行了封装处理,因此我们项目中直接使用ThreadPoolTaskExecutor即可。

项目中线程池使用案列

一、封装成工具类(我们老大的写法)

1.确保项目已导入spring相关的jar包

在这里插入图片描述

2.创建连接池配置类
@Configuration
public class ThreadPoolConfig {

  @Bean(name = "threadPoolTaskExecutor")
  public ThreadPoolTaskExecutor getThreadPoolTaskExecutor() {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    // 核心线程数
    taskExecutor.setCorePoolSize(10);
    // 最大线程数
    taskExecutor.setMaxPoolSize(100);
    // 阻塞队列长度
    taskExecutor.setQueueCapacity(100);
    // 空闲线程最大存活时间
    taskExecutor.setKeepAliveSeconds(200);
    // 拒绝策略
    taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    taskExecutor.initialize();
    return taskExecutor;
  }
}

图中设置参数可自行根据项目情况进行调整!

3.创建从Spring容器中获取Bean的工具类SpringContextHelper
@Component
public class SpringContextHelper implements ApplicationContextAware {

  private static ApplicationContext applicationContext;

  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    SpringContextHelper.applicationContext = applicationContext;
  }

  public static Object getBean(Class<?> clazz) throws BeansException {
    return applicationContext.getBean(clazz);
  }

  public static Object getBean(String name) throws BeansException {
    return applicationContext.getBean(name);
  }
}
4.创建一个通过反射执行方法的工具类ReflectionUtil
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;

public class ReflectionUtil {

  private static Logger logger = LoggerFactory.getLogger(ReflectionUtil.class);

  /**
   * *通过反射的方式调用对象方法
   *
   * @param object 服务对象
   * @param methodName 调用方法
   * @param args 方法参数(具有顺序性)
   */
  public static void invokeMethod(Object object, String methodName, Object[] args)
      throws Exception {
    logger.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);
    logger.debug(" invokeMethod end ");
  }
}
5.创建一个通用的异步任务类AsyncTask和工具类AsyncTaskUtil
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();
    }
  }
}
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.locks.ReentrantLock;

import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import com.xyebank.hzx.core.util.SpringContextHelper;

public class AsyncTaskUtil {
	
	private volatile static ThreadPoolTaskExecutor threadPoolTaskExecutor;
	
	private static final ReentrantLock LOCK = new ReentrantLock();
	
	private static ThreadPoolTaskExecutor getThreadPoolTaskExecutor() {
		if(threadPoolTaskExecutor == null) {
		    LOCK.lock();
			try {
				if(threadPoolTaskExecutor == null) {
					threadPoolTaskExecutor = (ThreadPoolTaskExecutor)SpringContextHelper.getBean("threadPoolTaskExecutor");
				}
			} finally {
			    LOCK.unlock();
			}
		}
		return threadPoolTaskExecutor;
	}
	
	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) {
		getThreadPoolTaskExecutor().execute(asyncTask);
	}
	
	public static <T> Future<T> asyncTask(Callable<T> callableTask) {
		return getThreadPoolTaskExecutor().submit(callableTask);
	}
	
}
6.创建待执行异步任务的类
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Test01AsyncTask implements Runnable {

  private static final Logger logger = LoggerFactory.getLogger(Test01AsyncTask.class);

  @Override
  public void run() {
    logger.info("异步任务1执行开始");
    try {
      // TODO 执行业务逻辑
      logger.info("异步任务1执行成功");
    } catch (Exception e) {
      logger.error(" 异步任务1执行出错", e);
    }
  }
}
7.测试异步调用执行任务
import com.weiyiji.async.AsyncTaskUtil;
import com.weiyiji.task.Test01AsyncTask;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
public class TestController {

  @GetMapping(value = "/test1")
  public String test1() {
    // 异步执行任务
    AsyncTaskUtil.asyncTask(new Test01AsyncTask());
    return "执行成功";
  }
}

执行结果如下:
线程池在项目中的实际使用_第2张图片
以上就是我们老大在项目中整合线程池的方法,其实也可以通过Spring中的注入注解进行处理,但是由于是封装成工具类(使用更方便)所以就手动从Spring容器中获取连接池对象了。

二、使用Spring提供的相关注解

  Spring官方不是提倡我们用注解开发嘛,因此Spring框架也提供了异步相关的注解如:@EnableAsync和@Async等。默认情况下,Spring将搜索相关的线程池定义:要么在上下文中搜索唯一的TaskExecutor bean,要么搜索名为“taskExecutor”的Executor bean。如果两者都无法解析(即我们没有手动配置相关的TaskExecutor bean),则将使用SimpleAsyncTaskExecutor来处理异步方法调用。
值得注意的是:

SimpleAsyncTaskExecutor:每次执行客户提交给它的任务时,它会启动新的线程,并允许开发者控制并发线程的上限(concurrencyLimit),从而起到一定的资源节流作用。默认时,concurrencyLimit取值为-1,即不启用资源节流。

SimpleAsyncTaskExecutor:不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程。并发大的时候会产生严重的性能问题。因此我们在实际项目中还是需要我们手动的去创建对应的线程池相关的Bean,跟上文一样创建一个ThreadPoolTaskExecutor的Bean

1.确保项目已导入spring相关的jar包

在这里插入图片描述

2.创建连接池配置类
@Configuration
public class ThreadPoolConfig {

  @Bean(name = "threadPoolTaskExecutor")
  public ThreadPoolTaskExecutor getThreadPoolTaskExecutor() {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    // 核心线程数
    taskExecutor.setCorePoolSize(10);
    // 最大线程数
    taskExecutor.setMaxPoolSize(100);
    // 阻塞队列长度
    taskExecutor.setQueueCapacity(100);
    // 空闲线程最大存活时间
    taskExecutor.setKeepAliveSeconds(200);
    // 拒绝策略
    taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
    taskExecutor.initialize();
    return taskExecutor;
  }
}

图中设置参数可自行根据项目情况进行调整!

3.启动类上添加@EnableAsync

我此处用SpringBoot项目测试,因此直接标注到启动类上了。

@EnableAsync
@SpringBootApplication
public class StudyApplication {

  public static void main(String[] args) {
    SpringApplication.run(StudyApplication.class, args);
  }
}
4.创建Test03AsyncTask任务类
@Component
@Async // 标注到类上说明类中所有方法都是异步方法,若标注到方法上则标注方法为异步方法
public class Test03AsyncTask {
  private static final Logger logger = LoggerFactory.getLogger(Test03AsyncTask.class);

  public void run() {
    try {
      // TODO 执行业务逻辑
      logger.info("{}:异步任务执行成功"+Thread.currentThread().getName());
      Thread.sleep(3000);
    } catch (Exception e) {
    }
  }

  public void run1() {
    try {
      // TODO 执行业务逻辑
      logger.info("{}:异步任务执行成功"+Thread.currentThread().getName());
      Thread.sleep(3000);
    } catch (Exception e) {
    }
  }
}
5.测试异步方法调用
import com.weiyiji.task.Test03AsyncTask;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
public class TestController {

  @Autowired private Test03AsyncTask test03AsyncTask;

  @GetMapping(value = "/test1")
  public String test1() {
    test03AsyncTask.run();
    test03AsyncTask.run1();
    return "执行成功";
  }
}
6.异步调用结果

在这里插入图片描述
如上图所示,异步任务执行成功!

你可能感兴趣的:(JAVA之实用基础,java,spring,开发语言)