zoukankan      html  css  js  c++  java
  • JUC线程池:3大方、7大参数、4种拒绝策略、CPU密集型、IO密集型、混合型、线程池最大线程数确定

    线程池

    3大方法、7大参数、4种拒绝 自定义线程池

    1、3大方法

    不推荐使用该3大方法,请使用ThreadPoolExecutor创建线程池

    • Executors.newSingleThreadExecutor() 单个线程
    • Executors.newFixedThreadPool(int num) 固定数线程的线程池
    • Executors.newCacheThreadPool() 根据任务的情况,而规定线程池中的线程数
    // newSingleThreadExecutor
    /*
    只允许一个线程执行,当核心线程数全部都在正在执行(第一个参数),进来的其他任务将会被加入到LinkedBlockingQueue队列中,由于这里的LinkedBlockingQueue的最大长度为Integer.MAX_VALUE,所以是无限大,由于会无限将任务存储到队列中,可能会造成OOM,最终会导致系统瘫痪,不安全。
    由于LinkedBlockingQueue可以很大,所以等待时间参数以及最大线程数的设置是无效的。
    */
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    
    // newFixedThreadPool
    /*
    只允许nThreads线程执行,当核心线程数全部都在正在执行(第一个参数),进来的其他任务将会被加入到LinkedBlockingQueue队列中,由于这里的LinkedBlockingQueue的最大长度为Integer.MAX_VALUE,所以是无限大,由于会无限将任务存储到队列中,可能会造成OOM,最终会导致系统瘫痪,不安全。
    由于LinkedBlockingQueue可以很大,所以等待时间参数以及最大线程数的设置是无效的。
    */
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    
    // newCachedThreadPool
    /*
    该方法,核心线程数为0,而可创建的最大线程数却为Integer.MAX_VALUE,无限创建线程,也会造成OOM,一个新线程会创建一个栈空间,而栈空间就会占内存。SynchronousQueue队列是一个不存储数据的队列,即放入即取出。当没有任务可以使用时,闲着的线程会等待60L秒后,如果还是没有任务执行,这个线程就会被销毁关闭。
    */
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

    通过3大方法,查看源码,发现这3大方法都new ThreadPoolExecutor该对象去创建线程池。阿里java开发手册强制规定,不可以使用Excutors工具类去创建线程池,因参数值为Integer.MAX_VALUE,最大线程支持21亿,这样高的并发量,会导致OOM (OutOfMemory内存溢出) 。阿里官方强烈推荐使用ThreadPoolExecutor来创建线程池

    2、7大参数

    ThreadPoolExecutor 类

    public ThreadPoolExecutor(int corePoolSize, // 线程核心数(一直存在的线程数量)
                              int maximumPoolSize, // 能创建的最大线程数
                              long keepAliveTime, // 设定除核心线程以外的线程,超过该时间未使用就关闭
                              TimeUnit unit, // 设定时间的单位
                              BlockingQueue<Runnable> workQueue
                              // 阻塞队列,用于存储等待使用线程的任务
                              ThreadFactory threadFactory, // 固定
                              RejectedExecutionHandler handler
                              // 拒绝超额任务的方式(4种)
                             ) {
            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;
    }

    执行过程原理

    任务一来,先检查核心线程是否已经被全部占用,如果没有全部占用,那么就使用核心线程数(核心线程数在初始化的时候就创建出来做准备的),如果已被全部占用,那么这个任务就会被加入到阻塞队列中。如果阻塞队列已满,那么就会创建新的线程,但是总线程数必须小于等于最大线程数。如果,最大线程数,每个线程都被占用,并且阻塞队列已满,那么就会根据拒绝方式返回不同的拒绝结果。

    3、4种拒绝

    // 最大限度任务:maximumPoolSize + workQueue
    new ThreadPoolExecutor.AbortPolicy(); // 拒绝超过最大限度任务,并抛出异常
    new ThreadPoolExecutor.CallerRunsPolicy(); // 哪儿来的任务回哪儿去,让你的原线程执行
    new ThreadPoolExecutor.DiscardPolicy(); // 丢弃超过最大限度任务,不抛出异常
    new ThreadPoolExecutor.DiscardOldestPolicy(); // 进来的线程任务,尝试与最早的任务竞争,竞争不到,丢弃,不抛出异常

    4、自定义线程池

    public class PoolTest {
        public static void main(String[] args) {
            // 获取该运行时,CPU的核数,作为最大线程数
            int processors = Runtime.getRuntime().availableProcessors();
            ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
                            1,
                            processors,
                            3,
                            TimeUnit.SECONDS,
                            new ArrayBlockingQueue<>(3),
                            Executors.defaultThreadFactory(),
                            new ThreadPoolExecutor.CallerRunsPolicy());
    
            for (int i = 1; i <= 8; i++) {
                poolExecutor.submit(()->{
                    System.out.println(Thread.currentThread().getName() + " OK");
                });
            }
    
            poolExecutor.shutdown();
        }
    }

    输出:

    pool-1-thread-2 OK
    pool-1-thread-3 OK
    pool-1-thread-1 OK
    main OK
    pool-1-thread-3 OK
    pool-1-thread-4 OK
    pool-1-thread-2 OK
    main OK

    CPU密集型 IO密集性 混合型

    线程池最大线程数应该如何定义?

    第一种:

    CPU密集型:最大线程数应该等于CPU核数+1,这样最大限度提高效率。

    // 通过该代码获取当前运行环境的cpu核数
    Runtime.getRuntime().availableProcessors();
    

    第二种:

    IO密集型:主要是进行IO操作,执行IO操作的时间较长,这时cpu出于空闲状态,导致cpu的利用率不高。线程数为2倍CPU核数。当其中的线程在IO操作的时候,其他线程可以继续用cpu,提高了cpu的利用率。

    第三种:

    混合型:如果CPU密集型和IO密集型执行时间相差不大那么可以拆分;如果两种执行时间相差很大,就没必要拆分了。

    第四种(了解):

    在IO优化中,线程等待时间所占比越高,需要线程数越多;线程cpu时间占比越高,需要越少线程数。因此:

    最佳线程数目=((线程等待时间+线程cpu时间)/ 线程cpu时间)* cpu数目

  • 相关阅读:
    Java第四章课后整理
    java第三章课后作业1
    Java第三章整理
    JAVA第二章课后作业
    JAVA第二章整理
    java课后作业1
    JAVA第一章整理实验
    JAVA大道至简第一章伪代码
    文本数据特征选取的四种方法
    时域分析与频率分析的比较
  • 原文地址:https://www.cnblogs.com/turbo30/p/13688205.html
Copyright © 2011-2022 走看看