美文网首页
java线程池

java线程池

作者: Darker_c772 | 来源:发表于2020-04-12 17:56 被阅读0次

线程池的概念

简单来说线程池就是一个管理线程的池子,当我们有任务需要执行无需创建线程,只需要将任务提交到线程池即可。

线程池的优点

  • 降低系统资源消耗,通过重用已存在的线程,降低线程创建和销毁造成的消耗。
  • 提高系统响应速度,当有任务到达时,通过复用已存在的线程,无需等待新线程的创建便能立即执行。
  • 方便线程并发数的管控。因为线程若是无限制的创建,可能会导致内存占用过多而产生OOM,并且会造成cpu过度切换(cpu切换线程是有时间成本的(需要保持当前执行线程的现场,并恢复要执行线程的现场))。
  • 提供更强大的功能,延时定时线程池。

线程池的创建

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              RejectedExecutionHandler handler) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), handler);
    }

核心参数的作用

  • corePoolSize: 线程池核心线程数最大值,核心线程是常驻线程即使没有可执行的任务也不会被回收。
  • maximumPoolSize: 线程池最大线程数大小。
  • keepAliveTime: 线程池中非核心线程空闲的存活时间大小。
  • 线程空闲存活时间单位
  • 存放任务的阻塞队列
  • 用于设置创建线程的工厂,可以给创建的线程设置有意义的名字,可方便排查问题。
  • 线城池的饱和策略事件,主要有四种类型。

线程池运行过程

线程池运行过程
  • 当我们向线程池提交任务时,线程池会判断存活的核心线程数是否小于线程数corePoolSize时,如果是线程池会创建一个核心线程去处理提交的任务。
  • 如果线程池核心线程数已满,即线程数已经大于等于corePoolSize,这时线程池会将任务放到一个任务队列workQueue排队等待执行。
  • 当线程池里面存活的线程数已经大于等于corePoolSize了,并且任务队列workQueue也满,判断线程数是否达到maximumPoolSize,如果没到达,创建一个非核心线程执行提交的任务,否则执行线程池拒绝策略。

拒绝策略

  • AbortPolicy(抛出一个异常,线程池默认的拒绝策略)
  • DiscardPolicy(直接丢弃任务)
  • DiscardOldestPolicy(丢弃队列里最老的任务,将当前这个任务继续提交给线程池)
  • CallerRunsPolicy(交给线程池调用所在的线程进行处理)

JUC下的线程池

  • newFixedThreadPool (固定数目线程的线程池)
  • newCachedThreadPool(可缓存线程的线程池)
  • newSingleThreadExecutor(单线程的线程池)
  • newScheduledThreadPool(定时及周期执行的线程池)

固定长度线程池

public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>(),
                                      threadFactory);
    }

特点

  • 长度固定,核心线程数和最大线程数相等。
  • 阻塞队列是无界阻塞队列LinkedBlockingQueue

工作机制

线程池运行过程

可缓存线程的线程池

   public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);
    }

特点

  • 核心线程数为0
  • 最大线程数为Integer.MAX_VALUE
  • 线程闲置存活时间为60秒

工作机制

线程池运行过程

单线程的线程池

  public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>(),
                                    threadFactory));
    }

特点

  • 长度固定,核心线程数和最大线程数相等,都为1。
  • 阻塞队列是无界阻塞队列LinkedBlockingQueue

工作机制

线程池运行过程

定时及周期执行的线程池

    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }

特点

  • 最大线程数为Integer.MAX_VALUE
  • 阻塞队列是DelayedWorkQueue

Java线程池配置原则

任务类型

  • CPU密集
    CPU密集型的话,一般配置CPU处理器个数+/-1个线程,所谓CPU密集型就是指系统大部分时间是在做程序正常的计算任务,例如数字运算、赋值、分配内存、内存拷贝、循环、查找、排序等,这些处理都需要CPU来完成。

  • IO密集
    IO密集型的话,是指系统大部分时间在跟I/O交互,而这个时间线程不会占用CPU来处理,即在这个时间范围内,可以由其他线程来使用CPU,因而可以多配置一些线程。

  • 混合型
    混合型的话,是指两者都占有一定的时间。

实例

public class ThreadPoolUtil {
    
    /**
     * 创建的线程设置有意义的名字,可方便排查问题。
     */
    private static ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
            .setNameFormat("demo-pool-%d").build();
    /**
     * 建议阻塞队列使用有界队列,既可以保证不会因为任务挤压导致系统崩溃,
     * 同时也有助于我们及时的发现问题,作出响应(例如当阻塞队列满时预警)。
     * 阻塞队列的最大值可根据实际业务场景自行设置
     */
    private  static ExecutorService executorService= new ThreadPoolExecutor(ioIntesivePoolSize(), ioIntesivePoolSize(),
            0L, TimeUnit.MILLISECONDS,
            new ArrayBlockingQueue<>(100),namedThreadFactory,new ThreadPoolExecutor.AbortPolicy());


    private ThreadPoolUtil() {

    }

    public static void exec(Runnable runnable){
        executorService.execute(runnable);
    }

    public static void submit(Callable callable){
        executorService.submit(callable);
    }
    /**
     * Each tasks blocks 90% of the time, and works only 10% of its
     *  lifetime. That is, I/O intensive pool
     * @return io密集的线程池
     */
    public static int ioIntesivePoolSize() {

        double blockingCoefficient = 0.9;
        return poolSize(blockingCoefficient);
    }


    /**
     * A computation-intensive task has a blocking coefficient of 0
     * @return cpu intesive Thread pool size
     */
    public static int cpuIntesivePoolSize() {

        double blockingCoefficient = 1;
        return poolSize(blockingCoefficient);
    }

    /**
     *
     * Number of threads = Number of Available Cores / (1 - Blocking
     * Coefficient) where the blocking coefficient is between 0 and 1.
     *
     * A computation-intensive task has a blocking coefficient of 0, whereas an
     * IO-intensive task has a value close to 1,
     * so we don't have to worry about the value reaching 1.
     *  @param blockingCoefficient the coefficient
     *  @return Thread pool size
     */
    public static int poolSize(double blockingCoefficient) {
        int numberOfCores = Runtime.getRuntime().availableProcessors();
        int poolSize = (int) (numberOfCores / (1 - blockingCoefficient));
        return poolSize;
    }
}

相关文章

网友评论

      本文标题:java线程池

      本文链接:https://www.haomeiwen.com/subject/yhkbmhtx.html