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

前言

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

线程池

1.线程池的选择

  JDK工具中一共给我们提供了多种线程池,包含固定长度的线程池、单线程线程池、以及可扩容的线程池等。但是这些线程池在我们实际工作的项目中都不实用的,那么有的童鞋要问了,为什么JDK提供的线程池还不实用呢,是因为阿里巴巴开发手册中并发处理章节里第四点明确表示了线程池的使用规范:
在这里插入图片描述
那么我们应该选择哪种线程池呢,其实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 "执行成功";
  }
}

执行结果如下:
在这里插入图片描述
以上就是我们老大在项目中整合线程池的方法,其实也可以通过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.异步调用结果

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

  • 17
    点赞
  • 124
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 好的,我知道了。关于线程池使用线程池主要用来优化线程的创建和销毁过程,减小系统开销,提高资源利用率。常见的场景包括:处理网络请求、IO相关的操作、定时任务等。 其,ThreadPoolExecutor是Java线程池的一个实现类,它提供了丰富的线程池的配置参数,允许我们灵活地创建、维护和控制线程池的行为。ThreadPoolExecutor的核心参数包括:核心线程数、最大线程数、工作队列、保活时间等。 其典型的使用方式为: 1.使用ThreadPoolExecutor构造函数创建线程池对象; 2.通过execute或submit方法提交任务; 3.根据实际情况调整线程池的配置参数。 线程池使用的好处是可以充分利用系统资源,同时保持线程的稳定性和可控性。但是如果使用不当,也有可能会导致系统性能下降,因此在实际应用必须根据实际情况进行配置和优化。 ### 回答2: 在实际项目线程池是一种用于管理和复用线程的机制。通过线程池,我们可以创建一组已经初始化的线程,并立即使这些线程处于等待(空闲)状态。当有新的任务到来时,可以直接将任务交给线程池来处理,而无需创建新的线程。这种方式可以减少线程的创建和销毁开销,提高线程的复用性和效率。 在实际项目,有一些场景需要使用线程池。例如: 1. 高并发的网络服务器:在服务器端处理大量的请求时,可以使用线程池来处理每个请求,从而提高服务器的响应能力和性能。 2. 多线程的数据处理:在数据处理任务较多的情况下,可以将这些任务交给线程池来处理,通过并发地执行任务,提高数据处理的效率。 3. 定时任务的调度:使用线程池可以很方便地实现定时任务的调度和执行,可以定期地执行一些后台任务,如数据备份、日志清理等。 ThreadPoolExecutor(线程池执行器)是Java提供的一个线程池实现类。它通过ThreadPoolExecutor构造函数的不同参数可以灵活地配置线程池的核心线程数、最大线程数、空闲线程存活时间、阻塞队列长度等属性,适应不同场景的线程池需求。ThreadPoolExecutor还提供了一些方法来提交任务、关闭线程池、获取线程池的状态等。 通过合理配置线程池的参数,可以使线程池在任务处理效率和资源占用上达到最佳的平衡。同时,使用线程池还有一些好处,如线程的生命周期得到了更好的管理,可以捕获并处理异常,线程的执行结果可以通过Future对象获取,任务的执行可以按优先级顺序进行等等。 总之,在实际项目,合理使用线程池可以提高程序的性能、可扩展性和稳定性。 ### 回答3: 在实际项目使用线程池有以下几个方面的考虑: 1. 提高性能和资源管理:线程池可以重复利用线程,避免频繁地创建和销毁线程带来的开销,提高系统性能。同时,线程池还可以根据系统负载情况动态调节线程数,防止系统资源过度占用。 2. 控制并发数:线程池可以限制同时执行的线程数量,防止系统因过多的线程而出现资源竞争和阻塞的问题。可以设置线程池的最大线程数和队列容量来控制并发数。 3. 提供任务调度和异步执行:线程池可以将任务按顺序或优先级进行调度,实现任务的异步执行。可以根据任务的优先级、类型等来安排执行顺序,提高系统的响应速度和实时性。 常见的使用场景有: 1. Web服务器:用于处理客户端的请求,通过线程池可以控制处理请求的并发数,避免服务器过度加载。 2. 数据库连接池:通过线程池管理数据库连接,避免频繁地创建和关闭数据库连接,提高数据库访问的效率。 3. 多线程任务处理:将大量的任务交给线程池异步处理,提高任务处理的效率。 ThreadPoolExecutor(线程池执行器)是Java线程池的实现类,它实现了ExecutorService接口,提供了线程池的常用功能和方法。通过ThreadPoolExecutor,可以自定义线程池的各种参数,如核心线程数、最大线程数、线程存活时间、拒绝策略等,并提供了一系列方法用于提交任务、关闭线程池、获取线程池状态等操作。通过合理配置ThreadPoolExecutor的参数,可以更好地满足项目需求,提高并发处理能力。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值