Spring Boot的多线程

本文详细探讨了线程池的工作原理,包括线程池的开启方式、不同策略下的队列选择,以及如何在Spring框架中实现多线程任务。通过具体的代码示例,展示了基于Spring注解和Java线程实现的多线程任务执行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

线程池

线程池开启

随着请求任务的不断增加,并发的压力逐渐增大,JVM会开启更多的线程,会出现以下java api描述的几种情况:

如果正在运行的线程少于corePoolSize,Executor总是喜欢添加一个新线程去执行请求,而不是将任务(Runnable)排队,直到达到corePoolSize。
如果运行的线程等于或大于corePoolSize,Executor总是喜欢将,后来的新的请求(不是线程)加进队列排队等候,而不是添加一个新的线程。
如果请求数量并发继续增大,以至于队列满,请求无法排队,则会创建一个新线程去处理;假设创建线程后总线程数超出maximumPoolSize就不再创建线程,如果创建不了线程,任务将被拒绝,也即并发太高的情况。

线程池的开启方式:
Executors可以理解为一个线程池的工具类,它方法有很多有用的静态方法。

  • public static <T> Callable<T> callable(Runnable task, T result)
    利用了一个RunnableAdapter的内部静态类,RunnableAdapter是一个适配器,利用了适配器模式。将一个Runnable接口封装为一个callable接口。

FutureTask的构造方法,public FutureTask(Runnable runnable, V result)就是一个将Runnable对象,封装成为Callable赋值给内部的Callable属性,进而完成对象的初始化。

  • public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)
    可以创建ThreadPoolExecutor(继承了AbstractExecutorService 又继承了ExecutorService又继承了Executor),创建的方法有三种,但是底层调用的都是
    public ThreadPoolExecutor(…) 构造器,构造器的参数说明:

int corePoolSize: 核心线程数量,池中要保持的线程数量,即使空闲,除非allowCoreThreadTimeOut参数被设置
int maximumPoolSize: 池中最大线程数量,当请求队列放满,核心线程不能及时处理新来的线程,线程工厂就继续开启线程直到达到maximumPoolSize
long keepAliveTime: 最大存活时间,当线程池中线程的数量大于corePoolSize,多余的空闲线程等待新任务的最大时间,如果超过keepAliveTime,线程对象就会被GC回收
TimeUnit unit:/时间单位
BlockingQueue workQueue: 同步队列,存放任务Runnable,这个队列只会存放execute提交的请求
ThreadFactory threadFactory: 线程工厂产生线程池
RejectedExecutionHandler handler:如果任务队列(BlockingQueue)排满并且线程超过最大容量(maximumPoolSize)时,新提交的任务会被拒绝(也即并发太高的情况),这时应该使用的处理器。

public ThreadPoolExecutor(
	int corePoolSize,// 核心线程数量,池中要保持的线程数量,即使空闲,除非allowCoreThreadTimeOut被设置
 	int maximumPoolSize, // 池中最大线程数量,当请求队列放满,核心线程不能及时处理新来的线程,线程工厂就继续开启线程直到达到maximumPoolSize
 	long keepAliveTime,// 最大存活时间,当线程池中线程的数量大于corePoolSize,多余的空闲线程等待新任务的最大时间,如果超过keepAliveTime,线程对象就会被GC回收
    TimeUnit unit,// 时间单位
    BlockingQueue<Runnable> workQueue, // 同步队列,存放任务Runnable,这个队列只会存放execute提交的请求
    ThreadFactory threadFactory,// 线程工厂产生线程池
    RejectedExecutionHandler handler) {...}                          

Executors基本上共4种生成线程池服务ExecutorService的方式: 1.newFixedThreadPool() 2.newCachedThreadPool() 3.newSingleThreadScheduledExecutor() 4.newScheduledThreadPool() 底层调用的都是ThreadPoolExecutor的构造方法,不同只是BlockingQueue队列的不同。

  1. 自然切换构建无限大的线程池策略:使用的队列是SynchronousQueue,java api对它的描述是,一种默认比较好的切换任务的方式(对线程的任务),不用刻意的处理他们。内部机制:当没有足够的线程来处理任务时,将任务排入任务队列会失败,所以总是开启出新的线程来处理他们,当处理的任务有内部依赖关系时可以避免锁定。自然切换通常需要无限大的最大线程池数量maximumPoolSize,当有大量任务来临时,已有线程来不及处理,就有可能产生无限多的线程。
  • Direct handoffs. A good default choice for a work queue is a {@link SynchronousQueue} that hands off tasks to threads without otherwise holding them. Here, an attempt to queue a task will fail if no threads are immediately available to run it, so a new thread will be constructed. This policy avoids lockups when handling sets of requests that might have internal dependencies. Direct handoffs generally require unbounded maximumPoolSizes to avoid rejection of new submitted tasks. This in turn admits the possibility of unbounded thread growth when commands continue to arrive on average faster than they can be processed.
  1. 构造无限大的队列策略:使用的队列是LinkedBlockingQueue,当所有的核心线程(corePoolSize)都忙时,构造一个没有预先定义大小的无界的队列,来存放新来的请求任务。所以比核心线程数量corePoolSize多的线程不会被开启,最大线程池数量maximumPoolSize参数也就不会生效。这种模式适用于,任务之间不能有相互依赖影响的关系,彼此之间独立的情况。这种情况适用于网页服务器和高并发请求的削峰。
  • Unbounded queues. Using an unbounded queue (for example a {@link LinkedBlockingQueue} without a predefined capacity) will cause new tasks to wait in the queue when all corePoolSize threads are busy. Thus, no more than corePoolSize threads will ever be created. (And the value of the maximumPoolSize therefore doesn't have any effect.) This may be appropriate when each task is completely independent of others, so tasks cannot affect each others execution; for example, in a web page server.While this style of queuing can be useful in smoothing out transient bursts of requests, it admits the possibility of unbounded work queue growth when commands continue to arrive on average faster than they can be processed.
  1. 有界队列策略:使用ArrayBlockingQueue,制定最大线程池数量maximumPoolSize可以防止系统资源耗尽但是比较难以平衡和把控。一般使用大队列和小池子可以有效的减少cpu利用率,os资源,上下文切换量。但是可能会人为的导致并发吞吐量下降。如果有大量的任务阻塞(如io阻塞),系统可能会花费比你预计更多的时间。或者小队列大池子可以提高cpu利用率,但是可能会遇到过多不可接受的调度开销,这也会降低并发量。
  • Bounded queues. A bounded queue (for example, an{@link ArrayBlockingQueue}) helps prevent resource exhaustion when used with finite maximumPoolSizes, but can be more difficult to tune and control. Queue sizes and maximum pool sizes may be traded off for each other: Using large queues and small pools minimizes CPU usage, OS resources, and context-switching overhead, but can lead to artificially low throughput. If tasks frequently block (for example if they are I/O bound), a system may be able to schedule time for more threads than you otherwise allow. Use of small queues generally requires larger pool sizes, which keeps CPUs busier but may encounter unacceptable scheduling overhead, which also decreases throughput.

拒绝的任务:当线程池服务关闭,或者当线程池使用有界的线程数量和任务队列(线程池达到最大容量,任务队列也满)。不论哪一种情况线程服务的execute方法都会调用RejectedExecutionHandler拒绝任务处理器的rejectedExecution方法。有几种已经定义好的处理策略:

AbortPolicy:抛出运行时异常
CallerRunsPolicy:调用execute自身的线程来执行新来的任务。这会降低新提交任务的速度。
DiscardPolicy:新来的任务会被抛弃
DiscardOldestPolicy:将队列中较早的任务抛弃。

Spring通过任务执行器(TaskExecutor)来实现多线程和并发编程。使用ThreadPoolTaskExecutor可实现一个基于线程池的TaskExecutor。而实际开发中任务一般是非阻碍的,即异步的,所以我们要在配置类中通过@EnableAsync开启对异步任务的支持,并通过在实际执行的Bean的方法中使用@Async注解声明其是一个异步任务。

此外,还提供一种Java的实现方式,多种方式去尝试如何去实现多线程。

一、基于Spring注解

定义配置类

import java.util.concurrent.Executor;

import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

/**
 * @Description: 配置类实现AsyncConfigurer接口,并重写getAsyncExecutor方法,并返回一个ThreadPoolTaskExecutor,
 * 这样我们就获得一个基于线程池TaskExecutor
 * @ClassName: CustomMultiThreadingConfig
 * @Author: 
 * @Date:
 */
@Configuration // 单例
@EnableAsync//利用@EnableAsync注解开启异步任务支持
public class CustomMultiThreadingConfig implements AsyncConfigurer{

    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(5);
        taskExecutor.setMaxPoolSize(10);
        taskExecutor.setQueueCapacity(25);
        taskExecutor.initialize();
        return taskExecutor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return AsyncConfigurer.super.getAsyncUncaughtExceptionHandler();
    }
    
}

创建线程任务

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
/**
 * @Description: 创建线程任务服务
 * @ClassName: CustomMultiThreadingService
 * @Author: 
 * @Date: 
 */
@Service
public class CustomMultiThreadingService {
    private Logger logger = LoggerFactory.getLogger(CustomMultiThreadingService.class);
    /**
     * @Description:通过@Async注解表明该方法是一个异步方法,
     * 如果注解在类级别上,则表明该类所有的方法都是异步方法,而这里的方法自动被注入使用ThreadPoolTaskExecutor作为TaskExecutor
     * @Title: 
     * @Date: 
     * @Author: 
     * @Throws
     * @param i
     */
    @Async
    public void executeAysncTask1(Integer i){
        logger.info("CustomMultiThreadingService ==> executeAysncTask1 method: 执行异步任务{} ", i);
    }
    
    /**
     * @Description:通过@Async注解表明该方法是一个异步方法,
     * 如果注解在类级别上,则表明该类所有的方法都是异步方法,而这里的方法自动被注入使用ThreadPoolTaskExecutor作为TaskExecutor
     * @Title: executeAsyncTask2
     * @Date: 
     * @Author: 
     * @Throws
     * @param i
     */
    @Async
    public void executeAsyncTask2(Integer i){
        logger.info("CustomMultiThreadingService ==> executeAsyncTask2 method: 执行异步任务{} ", i);
    }
}

调用线程任务

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.only.mate.springboot.multithreading.CustomMultiThreadingService;

/**
 * @Description:自定义多线程Controller
 * @ClassName: CustomMultiThreadingController
 * @Author: 
 * @Date: 
 */
@Controller
@RequestMapping(value="/multithreading")
public class CustomMultiThreadingController {
    @Autowired
    private CustomMultiThreadingService customMultiThreadingService;
    
    @ResponseBody
    @RequestMapping(value="/dotask")
    public String doTask() {
        for (int i=0;i<10;i++){
            customMultiThreadingService.executeAysncTask1(i);
            customMultiThreadingService.executeAsyncTask2(i);
        }
        
        return "success";
    }
}

二、基于Java线程实现

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @Description: 异步线程调度管理器
 * @ClassName: CustomAsyncScheduler
 * @Author: 
 * @Date: 
 */
public class CustomAsyncScheduler {
    private volatile static CustomAsyncScheduler instance;
    private static ThreadPoolExecutor chnlBackendQueryPool;

    private CustomAsyncScheduler() {
    }

    @SuppressWarnings({ "rawtypes", "static-access", "unchecked" })
    public static CustomAsyncScheduler getInstance() {
        if (instance == null) {// 自己实现单例
            synchronized (CustomAsyncScheduler.class) {
                if (instance == null) {
                    instance = new CustomAsyncScheduler();

                    BlockingQueue queue = new LinkedBlockingQueue();
                    chnlBackendQueryPool = new ThreadPoolExecutor(50, 100, 30, TimeUnit.SECONDS, queue);
                    chnlBackendQueryPool.allowCoreThreadTimeOut(true);
                    instance.setChnlBackendQueryPool(chnlBackendQueryPool);
                }
            }
        }
        return instance;
    }

    public ThreadPoolExecutor getChnlBackendQueryPool() {
        return chnlBackendQueryPool;
    }

    public static void setChnlBackendQueryPool(ThreadPoolExecutor chnlBackendQueryPool) {
        CustomAsyncScheduler.chnlBackendQueryPool = chnlBackendQueryPool;
    }

    public static void setInstance(CustomAsyncScheduler instance) {
        CustomAsyncScheduler.instance = instance;
    }

}

创建线程任务

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

/**
 * @Description: 创建线程任务服务
 * @ClassName: CustomMultiThreadingService
 * @Author: 
 * @Date: 
 */
@Service
public class CustomMultiThreadingService {
    private Logger logger = LoggerFactory.getLogger(CustomMultiThreadingService.class);
    /**
     * @Description:通过@Async注解表明该方法是一个异步方法,
     * 如果注解在类级别上,则表明该类所有的方法都是异步方法,而这里的方法自动被注入使用ThreadPoolTaskExecutor作为TaskExecutor
     * @Title: executeAysncTask1
     * @Date: 
     * @Author: 
     * @Throws
     * @param i
     */
    @Async
    public void executeAysncTask1(Integer i){
        logger.info("CustomMultiThreadingService ==> executeAysncTask1 method: 执行异步任务{} ", i);
    }
    
    /**
     * @Description:通过@Async注解表明该方法是一个异步方法,
     * 如果注解在类级别上,则表明该类所有的方法都是异步方法,而这里的方法自动被注入使用ThreadPoolTaskExecutor作为TaskExecutor
     * @Title: executeAsyncTask2
     * @Date:
     * @Author: 
     * @Throws
     * @param i
     */
    @Async
    public void executeAsyncTask2(Integer i){
        logger.info("CustomMultiThreadingService ==> executeAsyncTask2 method: 执行异步任务{} ", i);
    }
    
    /**
     * @Description: 异步线程调度管理器创建线程任务
     * @Title: executeAsyncTask3
     * @Date: 
     * @Author: 
     * @Throws
     * @param i
     */
    public void executeAsyncTask3(Integer i){
        CustomAsyncScheduler.getInstance().getChnlBackendQueryPool().execute(new Runnable() {
            @Override
            public void run() {
                logger.info("CustomMultiThreadingService ==> executeAsyncTask3 method: 执行异步任务{} ", i);                
            }
        });
        
    }
}

触发线程任务

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.only.mate.springboot.multithreading.CustomMultiThreadingService;

/**
 * @Description:自定义多线程Controller
 * @ClassName: CustomMultiThreadingController
 * @Author: 
 * @Date: 
 */
@Controller
@RequestMapping(value="/multithreading")
public class CustomMultiThreadingController {
    @Autowired
    private CustomMultiThreadingService customMultiThreadingService;
    
    @ResponseBody
    @RequestMapping(value="/dotask")
    public String doTask() {
        for (int i=0;i<10;i++){
            customMultiThreadingService.executeAysncTask1(i);
            customMultiThreadingService.executeAsyncTask2(i);
        }
        
        return "success";
    }
    
    @ResponseBody
    @RequestMapping(value="/dojob")
    public String doJob() {
        for (int i=0;i<10;i++){
            customMultiThreadingService.executeAysncTask1(i);
            customMultiThreadingService.executeAsyncTask2(i);
            customMultiThreadingService.executeAsyncTask3(i);
        }
        return "success";
    }
}

自己封装的一个线程池

package cm.soft.collect.util;

import cm.soft.collect.Config.LogConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

@Component
public class CustomThreadPool implements AutoCloseable {
    static volatile CustomThreadPool customThreadPool;

    @Autowired
    LogConfig log;

    private final ExecutorService threadPoolExecutor =
            new ThreadPoolExecutor(5, 100, 30000,
                                   TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(50000));

    private CustomThreadPool() {
    }

    public static CustomThreadPool getPool() {
        if (customThreadPool == null) {
            synchronized (CustomThreadPool.class) {
                if (customThreadPool == null) {
                    customThreadPool = new CustomThreadPool();
                }
            }
        }

        return customThreadPool;
    }

    public ExecutorService getService() {
        return threadPoolExecutor;
    }

    @Override
    @PreDestroy
    public void close() {
        if (threadPoolExecutor != null&&!threadPoolExecutor.isShutdown()) {
            threadPoolExecutor.shutdown();
            log.info("关闭线程池");
        }
    }
}


### 回答1: Spring Boot支持多线程编程,主要有以下几种方式: 1. 继承Thread类或实现Runnable接口 这是最基本的多线程编程方式,可以在Spring Boot中使用。开发者可以通过继承Thread类或实现Runnable接口,重写run()方法实现多线程逻辑。示例代码: ```java public class MyThread extends Thread { @Override public void run() { // 多线程逻辑 } } public class MyRunnable implements Runnable { @Override public void run() { // 多线程逻辑 } } // 使用 new MyThread().start(); new Thread(new MyRunnable()).start(); ``` 2. 使用线程池 线程池可以有效地管理多个线程,避免创建和销毁线程的开销。Spring Boot提供了ThreadPoolTaskExecutor类,可以用来创建和管理线程池。示例代码: ```java @Configuration @EnableAsync public class AsyncConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(10); executor.setMaxPoolSize(20); executor.setQueueCapacity(30); executor.initialize(); return executor; } } // 使用 @Async public void doSomething() { // 多线程逻辑 } ``` 3. 使用CompletableFuture CompletableFuture是Java 8引入的异步编程方式,可以很方便地实现多线程编程。Spring Boot也提供了对CompletableFuture的支持。示例代码: ```java public CompletableFuture<String> doSomething() { CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { // 多线程逻辑 return "result"; }); return future; } ``` 以上是Spring Boot多线程编程的三种方式,开发者可以根据具体的业务场景选择不同的方式。 ### 回答2: Spring Boot是一个用于快速构建基于Spring框架的应用程序的工具。在Spring Boot中,可以轻松地实现多线程的功能。 Spring Boot提供了多种方式来实现多线程。一种常用的方式是使用`@Async`注解来标记一个方法为异步方法。在使用`@Async`注解之后,该方法将会在一个单独的线程中执行,而不会阻塞主线程。通过使用这个注解,可以在应用程序中执行耗时操作,而不会影响其他的业务逻辑。需要注意的是,要实现异步方法,还需要在应用程序的主类上添加`@EnableAsync`注解。 除了使用`@Async`注解外,Spring Boot还提供了`ThreadPoolTaskExecutor`类来方便地创建线程池。通过配置线程池的大小、最大线程数等参数,可以实现更加灵活和高效的多线程处理。可以在应用程序的配置文件中添加以下配置来创建线程池: ```java @Configuration @EnableAsync public class AsyncConfig { @Bean(name = "taskExecutor") public Executor taskExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(25); executor.setThreadNamePrefix("taskExecutor-"); executor.initialize(); return executor; } } ``` 上述配置创建了一个线程池,核心线程数为5,最大线程数为10,队列容量为25。通过将`@Async`注解和`taskExecutor`作为参数添加到方法中,即可实现多线程的功能。 综上所述,Spring Boot提供了多种方式来实现多线程。使用`@Async`注解可以快速实现异步方法,而`ThreadPoolTaskExecutor`类则提供了更加灵活和高效的线程池配置。这些功能可以帮助我们提高应用程序的性能和并发处理能力。 ### 回答3: Spring Boot多线程允许开发者在应用程序中使用并发处理来提高性能和效率。它基于Java的多线程机制,但通过Spring Boot可以更加方便地进行配置和管理。 Spring Boot使用了Java的Executor框架来处理线程池和线程管理。开发者可以通过@EnableAsync注解启用异步方法和@Async注解将方法标记为异步执行。这样在调用该方法时,Spring Boot会自动创建一个新的线程执行该方法,而当前线程则不会被阻塞。 使用多线程可以提高应用程序的响应性能,特别是在处理一些耗时的操作时。通过异步方法,可以将一些需要等待的操作转移到后台线程中执行,不影响主线程继续执行其他操作。这对于处理大量请求或者IO密集型的任务非常有用。 在配置多线程时,开发者可以指定线程池的大小、最大线程数、线程的生命周期等参数。这些参数可以根据具体的应用场景进行调整,以达到最佳的性能和资源利用。同时,Spring Boot还提供了一些方便的工具类和注解,用于处理线程间的数据共享和同步,如通过ThreadLocal存储线程局部变量,通过@Lock注解实现对指定资源的加锁。 总之,Spring Boot多线程提供了一种便捷的方式来管理并发处理,使得开发者能够更加轻松地实现并行执行和异步操作。它可以大大提高应用程序的性能和响应能力,适用于处理一些耗时的任务和IO密集型的操作。但同时也需要注意线程安全和资源管理的问题,合理配置和使用多线程,才能充分发挥其潜力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值