java线程池

线程池篇

建议使用ThreadPoolExecutor类进行线程池的创建,更加细粒度的管理自己所使用的线程池,对线程池的分配也根据自己的实际情况来具体的控制

如果使用线程池可能会导致OOM(outofMemoryError)

核数的获取

获取当前电脑的核数:Runtime.getRuntime().availableProcessors();

ThreadPoolExecutor

线程池最底层的类ThreadPoolExecutor

ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
    2,
    /*
     *   IO 密集型 CPU*2
     *   CPU密集型 CPU核数+1 (逻辑处理器+1)
     * */
    5,
    60L,
    TimeUnit.SECONDS,
    //默认是Integer.Max_Value
    new LinkedBlockingQueue<Runnable>(3),
    Executors.defaultThreadFactory(),
    /**
     * abort RejectedExecutionException
     * CallerRunsPolicy 会回退到上一个线程,比如main线程
     * DiscardPolicy 放心新来的
     * DiscardOldestPolicy 放弃阻塞队列中最开始的那个
     */
    new ThreadPoolExecutor.DiscardOldestPolicy());

Executors工具类

newWorkStealingPool(1.8)

public static ExecutorService newWorkStealingPool(int parallelism) {
    return new ForkJoinPool
        (parallelism,
         ForkJoinPool.defaultForkJoinWorkerThreadFactory,
         null, true);
}

public ForkJoinPool(int parallelism,
                    ForkJoinWorkerThreadFactory factory,
                    UncaughtExceptionHandler handler,
                    boolean asyncMode) {
    this(checkParallelism(parallelism),
         checkFactory(factory),
         handler,
         asyncMode ? FIFO_QUEUE : LIFO_QUEUE,
         "ForkJoinPool-" + nextPoolId() + "-worker-");
    checkPermission();
}

private ForkJoinPool(int parallelism,
                     ForkJoinWorkerThreadFactory factory,
                     UncaughtExceptionHandler handler,
                     int mode,
                     String workerNamePrefix) {
    this.workerNamePrefix = workerNamePrefix;
    this.factory = factory;
    this.ueh = handler;
    this.config = (parallelism & SMASK) | mode;
    long np = (long)(-parallelism); // offset ctl counts
    this.ctl = ((np << AC_SHIFT) & AC_MASK) | ((np << TC_SHIFT) & TC_MASK);
}
//分支合并线程池

newFixedThreadPool

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
  • 创建固定的线程数,创建的初始值和最大值都是一个
  • 创建的阻塞队列无上限

newCachedThreadPool

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}
  • 带缓存的线程池数,当开始的时候是0,最大是Integer.MAX_VALUE
  • 最长六十秒不被使用则被回收

newSingleThreadPool

public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
    return new DelegatedScheduledExecutorService
        (new ScheduledThreadPoolExecutor(1));
}
  • 创建一个单个的线程池(里面只有一个)

newScheduledThreadPool

public static ScheduledExecutorService newScheduledThreadPool(
    int corePoolSize, ThreadFactory threadFactory) {
    return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}

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

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         threadFactory, defaultHandler);
}
  • 创建一个带时间的线程池,使用的是DelayedWorkQueue拒绝阻塞队列

线程池的使用

  • execute():void
  • submit():Future

对无返回值的线程执行

try {
    for (int i = 0; i < 9; i++) {
        poolExecutor.execute(() -> {
            System.out.println(Thread.currentThread().getName() + "\t");
        });
    }
} catch (Exception e) {
    e.printStackTrace();
} finally {
    poolExecutor.shutdown();
}

对有返回值的线程执行

poolExecutor.submit(new FutureTask<>(() -> 1024)).get();

线程池的执行流程

  1. 首先来的线程任务开始开启小于等于corePoolSize的线程进行执行
  2. 如果有些线程还是没执行完,但是有新的任务来了,那么将会进入阻塞队列
  3. 如果阻塞队列满了,就会去开启新的线程来执行任务,线程<=MaxiNumPoolSize
  4. 当大于corePoolSize的线程在KeepAliveTime内没被使用则会被释放
  5. 如果开启了最大的线程还是有任务没有被执行,则会进行使用拒绝策略进行对新任务的拒绝

线程池具体参数的配置*

线程池的拒绝策略

当等待队列满了&线程池的最大线程数也达到了,当出现新的任务时,会执行拒绝策略

new ThreadPoolExecutor.CallerRunsPolicy()
  • abortPolicy 抛出RejectedExecutionException,阻止系统正常运行
  • CallerRunsPolicy 会回退到上一个线程(调用它的线程),比如main线程
  • DiscardPolicy 放弃新来的task,如果允许任务丢失,则是最好的一种策略
  • DiscardOldestPolicy 放弃阻塞队列中最开始的那个

上述策略皆实现了RejectedExecutionHandler接口

线程池最大线程数的设定

CPU密集型

  • CPU核数+1个线程的线程池

IO密集型

  • IO密集型任务线程并不是一直在执行任务,则应尽可能多的线程,CUP核数*2
  • CUP核数 / 1-阻塞系数 (阻塞系数在0.8~0.9之间)
    • 比如8核CPU: 4/1-0.9 = 40
posted @ 2020-03-11 18:26  atomFix  阅读(291)  评论(0编辑  收藏  举报