0
点赞
收藏
分享

微信扫一扫

Java并发:线程池的基本原理总结

西曲风 2022-01-06 阅读 83

一、为何选择线程池,不用时创建线程呢?

线程池作为池化技术的一种实践,本质上也是同样的思想,提前备好资源以备不时之需。因此,线程池相比较任务出现再创建线程具有以下的优点:

  • 降低资源损耗:通过重复利用已创建的线程降低线程创建和销毁造成的损耗。
  • 提高响应速度:当任务到达时,可以不需要等到线程创建完毕就能立即执行。
  • 提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性。

二、当一个任务进入线程池,发生了什么?

在这里插入图片描述
如上图所示,一个新任务进入到线程池时,处理流程如下:

判断核心线程池的线程是否都在执行任务,如果不是,则创建一个工作线程来执行此任务
当核心线程池已满时,进入工作队列等待
当工作队列已满,判断线程池是否达到最大线程数,不是,则创建一个工作线程来执行此任务
如果工作队列和线程池都满了,则交给饱和策略来处理这个任务

三、如何使用一个线程池?

线程池的创建

我们在创建线程池的过程中,使用底层的new ThreadPollExecutor,


package com.markor.template.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

/**
 * @describe:
 * @author: caichangmeng <modules@163.com>
 * @since: 2018/10/22
 */
@Configuration
public class ThreadConfig {
    @Bean
    public ThreadPoolTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        taskExecutor.setCorePoolSize(3);
        taskExecutor.setMaxPoolSize(7);
        taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
        return taskExecutor;
    }
}


其源码如下

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0) //检查输入参数是否异常
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException(); //检查工作队列、线程工厂、拒绝策略是否空指针
              //对属性开始赋值
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

可以看到,线程池创建的七大参数:

  • corePoolSize:线程池的基本大小
  • runnableTaskQueue:任务队列
  • ThreadFactory:用于设置创建线程的工厂,可以通过线程工厂来给线程设置有意义的名字
  • RejectedExecutionHandler:饱和策略,默认情况下是AbortPolicy,JDK1.5提供了一下四种策略
  1. AbortPolicy:直接抛出异常
  2. CallerRunsPolicy:只用调用者所在线程来运行任务
  3. DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务
  • DiscardPolicy:不处理丢弃
  • keepAliveTime:线程池的工作线程空闲后,保持存活的时间
  • TimeUnit:线程活动保持时间的单位

向线程池提交任务

目前分为executre()和submit()方法

//execute()提交不需要返回值的任务
threadPool.execute(new Runnable() {
            @Override
            public void run() {
                //TODO 
            }
        });
//submit()提交需要返回值的任务
Callable myCallable = new Callable() {
  @Override
  public String call() throws Exception {
    Thread.sleep(3000);
    return "call方法返回值";
  }
};
Future future = threadPool.submit(myCallable);
String futureRes = future.get();

关闭线程池

//会停止所有正在执行任务的线程
threadPool.shutdownNow();
//只中断没有执行任务的线程
threadPool.shudown();

合理的配置线程池

1.线程数的配置:
目前公司的项目,用简单的HttpServer暴露prometheus的metrics的内容,设置的是fixThreadPool,线程数设置为5,就是根据Ncpu * 2得到的经验值。

2.建议使用有界队列
有界队列能增加系统的稳定性和预警能力,可以根据需要设大一点儿,比如几千。

举报

相关推荐

0 条评论