springboot多线程

多线程实现

1、要写一个配置类开启多线程

配置类实现AsyncConfigurer接口,并重写getAsyncExecutor()方法返回一个Executor,并用@EnableAsync注解标注。

@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
    @Override
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        // 核心线程大小
        threadPoolTaskExecutor.setCorePoolSize(8);
        // 最大线程大小
        threadPoolTaskExecutor.setMaxPoolSize(16);
        // 队列大小
        threadPoolTaskExecutor.setQueueCapacity(50);
        // 初始化
        threadPoolTaskExecutor.initialize();
        return threadPoolTaskExecutor;
    }
}

2、在需要多线程执行的方法上标注@Async注解

如果@Async标注在类上,则该类的所有方法都是异步方法。

@Service
@Async
public class AsyncService {

    public void fun1(int i) {
        System.out.println("fun1----" + i);
    }

    public void fun2(int i) {
        System.out.println("fun2====" + i);
    }
}

3、测试效果

@Controller
@RequestMapping("/hello")
public class HelloController {

    @Autowired
    private AsyncService asyncService;

    @GetMapping("/testAsync")
    public String testAsync() {
        for (int i = 0; i < 100; i++) {
            asyncService.fun1(i);
            asyncService.fun2(i);
        }
        return "success";
    }
}

访问该接口,输出结果就会看到多线程效果

// 部分输出结果
fun1----0
fun1----4
fun2====4
fun1----5
fun2====5
fun1----6
fun2====6
fun1----7
fun2====7
fun1----8
fun2====8
fun1----9
fun2====9
fun1----10

实际输出中的报错

抛出RejectedExecutionException拒绝执行异常。

java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@24a8a5ec rejected from java.util.concurrent.ThreadPoolExecutor@6719f3a5[Running, pool size = 16, active threads = 16, queued tasks = 50, completed tasks = 0]
	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063) ~[na:1.8.0_161]
	at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830) [na:1.8.0_161]
	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379) [na:1.8.0_161]
	at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134) ~[na:1.8.0_161]

要分析报错的原因首先要指导Executor的执行原理是什么。

ThreadPoolTaskExecutor的一些参数

在配置类中配置了一些ThreadPoolTaskExecutor的corePoolSize核心线程池、maxPoolSize最大线程池、queneCapacity队列大小。ThreadPoolTaskExecutor会根据corePoolSize和maxPoolSize来调整线程池大小。

  • 当新任务执行时,如果运行的线程小于corePoolSize就会创建新的线程,即使其他线程是空闲状态。
  • 如果创建的线程数量达到了corePoolSize,就会把新任务放到队列中等待执行。
  • 如果队列满了,并且运行的线程小于maxPoolSize,就会创建新的线程。
  • 如果线程达到了maxPoolSize,且队列也满了,再开启新的任务就会报错RejectedExecutionException
  • 如果corePoolSize等于maxPoolSize,表示创建了固定大小的线程池。
  • 如果设置maxPoolSize的值为Integer.MAX_VALUE表示创建了无界的线程池

队列的三种策略

直接提交

工作队列的默认选项是synchronousQueue,它将任务直接提交给线程而不保持它们。在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。直接提交通常要求无界maximumPoolSizes以避免拒绝新提交的任务。当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增加的可能性。
无界队列

使用无界队列(例如,不具有预定义容量的LinkedBlockingQueue)将导致在所有corePoolSize线程都忙时新任务在队列中等待。这样,创建的线程就不会超过corePoolSize(因此,maximumPoolSize的值也就无效了)。
有界队列

当使用有限的maximumPoolSizes时,有界队列(如ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度的降低CPU使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞,则系统可能为超过您许可的更多线程安排时间,使用小型队列通常要求较大的池大小,CPU使用率较高,但是可能遇到不可接受的调度开销,这样可会降低吞吐量。

此外,引发RejectedExecutionException的另一种原因是,显式的调用了线程池的shutdown()方法。

参考:

https://juejin.im/post/5c344d55f265da617974ff4a

https://blog.csdn.net/wzy_1988/article/details/38922449

posted @ 2019-12-12 15:54  ununie  阅读(1347)  评论(0编辑  收藏  举报