精华内容
下载资源
问答
  • 参考文章: 1.浅谈线程池ThreadPoolExecutor核心参数 https://www.cnblogs.com/stupid-chan/p/9991307.html 2.Java线程池 ThreadPoolExecutor(一)线程池的核心方法以及原理 ... 3.Java 中的几种线程池,你之前用...

     

     

    参考文章:

    1.浅谈线程池ThreadPoolExecutor核心参数

    https://www.cnblogs.com/stupid-chan/p/9991307.html

    2.Java线程池 ThreadPoolExecutor(一)线程池的核心方法以及原理

    https://blog.csdn.net/m0_37506254/article/details/90574038

    3.Java 中的几种线程池,你之前用对了吗

    https://www.cnblogs.com/fengzheng/p/9297602.html

    4.线程池异常处理之重启线程处理任务

     https://www.cnblogs.com/hapjin/p/10240863.html

     

      整理下线程池的相关知识。阿里巴巴的规范是不允许使用Java提供的 Executors 返回的线程池,因为默认的线程池都存在一定的问题。本文主要从以下几个方面进行总结

     

    1.默认线程池的问题

    2.线程池的核心参数

    3.线程池的相关问题

    4.手动创建线程池

     

     

    默认线程池的问题

    如果使用 Executors 去创建线程池,使用阿里巴巴的插件会自动进行提示,

    提示如下 :

    说明 Java,默认提供的4种线程池创建方式都是不安全的。先看下默认的线程池创建方式的问题:

     

    单线程线程池

    ExecutorService singleThreadPool = Executors.newSingleThreadExecutor(Executors.defaultThreadFactory());
        /**
         * Creates an Executor that uses a single worker thread operating
         * off an unbounded queue, and uses the provided ThreadFactory to
         * create a new thread when needed. Unlike the otherwise
         * equivalent {@code newFixedThreadPool(1, threadFactory)} the
         * returned executor is guaranteed not to be reconfigurable to use
         * additional threads.
         *
         * @param threadFactory the factory to use when creating new
         * threads
         *
         * @return the newly created single-threaded Executor
         * @throws NullPointerException if threadFactory is null
         */
        public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
            return new FinalizableDelegatedExecutorService
                (new ThreadPoolExecutor(1, 1,
                                        0L, TimeUnit.MILLISECONDS,
                                        new LinkedBlockingQueue<Runnable>(),
                                        threadFactory));
        }

    再向下跟踪

       /**
         * Creates a new {@code ThreadPoolExecutor} with the given initial
         * parameters and default rejected execution handler.
         *
         * @param corePoolSize the number of threads to keep in the pool, even
         *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
         * @param maximumPoolSize the maximum number of threads to allow in the
         *        pool
         * @param keepAliveTime when the number of threads is greater than
         *        the core, this is the maximum time that excess idle threads
         *        will wait for new tasks before terminating.
         * @param unit the time unit for the {@code keepAliveTime} argument
         * @param workQueue the queue to use for holding tasks before they are
         *        executed.  This queue will hold only the {@code Runnable}
         *        tasks submitted by the {@code execute} method.
         * @param threadFactory the factory to use when the executor
         *        creates a new thread
         * @throws IllegalArgumentException if one of the following holds:<br>
         *         {@code corePoolSize < 0}<br>
         *         {@code keepAliveTime < 0}<br>
         *         {@code maximumPoolSize <= 0}<br>
         *         {@code maximumPoolSize < corePoolSize}
         * @throws NullPointerException if {@code workQueue}
         *         or {@code threadFactory} is null
         */
        public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
                                  long keepAliveTime,
                                  TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue,
                                  ThreadFactory threadFactory) {
            this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
                 threadFactory, defaultHandler);
        }
    

     

        可以看到内部调用了线程池的核心创建方法,newSingleThreadExecutor  创建出来的单线程线程池  最主要的问题,是因为使用了 new LinkedBlockingQueue<Runnable>() 作为等待队列,该队列为无界队列,会导致堆积大量请求线程,从而导致OOM.

     

    固定大小线程池

    ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10,Executors.defaultThreadFactory());
    /**
         * Creates a thread pool that reuses a fixed number of threads
         * operating off a shared unbounded queue, using the provided
         * ThreadFactory to create new threads when needed.  At any point,
         * at most {@code nThreads} threads will be active processing
         * tasks.  If additional tasks are submitted when all threads are
         * active, they will wait in the queue until a thread is
         * available.  If any thread terminates due to a failure during
         * execution prior to shutdown, a new one will take its place if
         * needed to execute subsequent tasks.  The threads in the pool will
         * exist until it is explicitly {@link ExecutorService#shutdown
         * shutdown}.
         *
         * @param nThreads the number of threads in the pool
         * @param threadFactory the factory to use when creating new threads
         * @return the newly created thread pool
         * @throws NullPointerException if threadFactory is null
         * @throws IllegalArgumentException if {@code nThreads <= 0}
         */
        public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
            return new ThreadPoolExecutor(nThreads, nThreads,
                                          0L, TimeUnit.MILLISECONDS,
                                          new LinkedBlockingQueue<Runnable>(),
                                          threadFactory);

     

    也和单线程线程池一样的问题, 是因为使用了  new LinkedBlockingQueue<Runnable>() 作为等待队列,该队列为无界队列,会导致堆积大量请求线程,从而导致OOM.

     

     

    缓存型线程池

    ExecutorService cachedThreadPool = Executors.newCachedThreadPool(Executors.defaultThreadFactory());

    向下跟踪

    /**
     * Creates a thread pool that creates new threads as needed, but
     * will reuse previously constructed threads when they are
     * available, and uses the provided
     * ThreadFactory to create new threads when needed.
     * @param threadFactory the factory to use when creating new threads
     * @return the newly created thread pool
     * @throws NullPointerException if threadFactory is null
     */
    public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);
    

    线程池的最大线程大小 max 为 Integer 上限,会创建大量的等待线程,从而引发OOM

     

    延迟执行线程池

    public void scheduleThreadPool() throws Exception{
    
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10, Executors.defaultThreadFactory());
        scheduledExecutorService.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("666" + new Date());
            }
        }, 4, TimeUnit.SECONDS);
    
        scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                System.out.println("777" + new Date());
            }
        }, 1, 4, TimeUnit.SECONDS);
    
        Thread.sleep(1000 * 60);
        scheduledExecutorService.shutdown();
    }
    

     

    向下跟踪代码: 

    /**
         * Creates a thread pool that can schedule commands to run after a
         * given delay, or to execute periodically.
         * @param corePoolSize the number of threads to keep in the pool,
         * even if they are idle
         * @param threadFactory the factory to use when the executor
         * creates a new thread
         * @return a newly created scheduled thread pool
         * @throws IllegalArgumentException if {@code corePoolSize < 0}
         * @throws NullPointerException if threadFactory is null
         */
        public static ScheduledExecutorService newScheduledThreadPool(
                int corePoolSize, ThreadFactory threadFactory) {
            return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
        }

    跟踪 ScheduledThreadPoolExecutor(corePoolSize, threadFactory);  构造方法:

        /**
         * Creates a new {@code ScheduledThreadPoolExecutor} with the
         * given core pool size.
         *
         * @param corePoolSize the number of threads to keep in the pool, even
         *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
         * @throws IllegalArgumentException if {@code corePoolSize < 0}
         */
        public ScheduledThreadPoolExecutor(int corePoolSize) {
            super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
                  new DelayedWorkQueue());
        }

    最后看到 线程池的最大线程大小 max 为 Integer 上限,会创建大量的等待线程,从而引发OOM

     

    结论:

       说明默认的4种线程池都多多少少存在问题 !! 

     

     

    ============================

     

    线程池的核心参数

       看到上面的默认线程池都用到了 ThreadPoolExecutor  这个类,这个类也是手动创建线程的核心类

    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>(),
                                  threadFactory);

     

    看下最后的最终构造函数:

    /**
         * Creates a new {@code ThreadPoolExecutor} with the given initial
         * parameters.
         *
         * @param corePoolSize the number of threads to keep in the pool, even
         *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
         * @param maximumPoolSize the maximum number of threads to allow in the
         *        pool
         * @param keepAliveTime when the number of threads is greater than
         *        the core, this is the maximum time that excess idle threads
         *        will wait for new tasks before terminating.
         * @param unit the time unit for the {@code keepAliveTime} argument
         * @param workQueue the queue to use for holding tasks before they are
         *        executed.  This queue will hold only the {@code Runnable}
         *        tasks submitted by the {@code execute} method.
         * @param threadFactory the factory to use when the executor
         *        creates a new thread
         * @param handler the handler to use when execution is blocked
         *        because the thread bounds and queue capacities are reached
         * @throws IllegalArgumentException if one of the following holds:<br>
         *         {@code corePoolSize < 0}<br>
         *         {@code keepAliveTime < 0}<br>
         *         {@code maximumPoolSize <= 0}<br>
         *         {@code maximumPoolSize < corePoolSize}
         * @throws NullPointerException if {@code workQueue}
         *         or {@code threadFactory} or {@code handler} is null
         */
        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.corePoolSize = corePoolSize;
            this.maximumPoolSize = maximumPoolSize;
            this.workQueue = workQueue;
            this.keepAliveTime = unit.toNanos(keepAliveTime);
            this.threadFactory = threadFactory;
            this.handler = handler;
        }

     

    线程池的核心参数为以下7个

    int corePoolSize,

    int maximumPoolSize,

    long keepAliveTime,

    TimeUnit unit,

    BlockingQueue<Runnable> workQueue,

    ThreadFactory threadFactory,

    RejectedExecutionHandler handler

    下面一一进行解释

     

    • int corePoolSize

    核心线程数,当有任务进来的时候,如果当前线程数还未达到 corePoolSize 个数,则创建核心线。

        默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,

    核心线程有几个特点:

    1、当线程数未达到核心线程最大值的时候,新任务进来,即使有空闲线程,也不会复用,仍然新建核心线程;

    2、核心线程一般不会被销毁,即使是空闲的状态,但是如果通过方法 allowCoreThreadTimeOut(boolean value) 设置为 true 时,超时也同样会被销毁;

    3、生产环境首次初始化的时候,可以调用 prestartCoreThread() / prestartAllCoreThreads() 方法 ,来预先创建所有核心线程,避免第一次调用缓慢;

     

    • int maximumPoolSize

        除了有核心线程外,有些策略是当核心线程占满(无空闲)的时候,还会创建一些临时的线程来处理任务,maximumPoolSize 就是核心线程 + 临时线程的最大上限。临时线程有一个超时机制,超过了设置的空闲时间没有事儿干,就会被销毁

     

    • long keepAliveTime 

          表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。

        但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
     

    • TimeUnit unit

    参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:

    TimeUnit.DAYS;               //天
    TimeUnit.HOURS;             //小时
    TimeUnit.MINUTES;           //分钟
    TimeUnit.SECONDS;           //秒
    TimeUnit.MILLISECONDS;      //毫秒
    TimeUnit.MICROSECONDS;      //微妙
    TimeUnit.NANOSECONDS;       //纳秒

     

    • BlockingQueue<Runnable> workQueue

     一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响。

    队列分为有界队列和无界队列。

       有界队列:队列的长度有上限,当核心线程满载的时候,新任务进来进入队列,当达到上限,有没有核心线程去即时取走处理,这个时候,就会创建临时线程。(警惕临时线程无限增加的风险)

       无界队列:队列没有上限的,当没有核心线程空闲的时候,新来的任务可以无止境的向队列中添加,而永远也不会创建临时线程。(警惕任务队列无限堆积的风险)

    除此之外,这里的阻塞队列有以下几种选择:

    1、ArrayBlockingQueue:基于数组的先进先出,创建时必须指定大小,超出直接corePoolSize个任务,则加入到该队列中,只能加该queue设置的大小,其余的任务则创建线程,直到(corePoolSize+新建线程)> maximumPoolSize。

    2、LinkedBlockingQueue:基于链表的先进先出,无界队列。超出直接corePoolSize个任务,则加入到该队列中,直到资源耗尽。

    3、SynchronousQueue:这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。

     ArrayBlockingQueue和PriorityBlockingQueue使用较少,一般使用LinkedBlockingQueue 和 Synchronous。线程池的排队策略与BlockingQueue有关。
     

    • ThreadFactory threadFactory

        它是一个接口,用于实现生成线程的方式、定义线程名格式、是否后台执行等等.

       可以用 Executors.defaultThreadFactory() 默认的实现即可,

       也可以用 Guava 等三方库提供的方法实现,

       如果有特殊要求的话可以自己定义。它最重要的地方应该就是定义线程名称的格式,便于排查问题了吧

     

    • RejectedExecutionHandler handler

        当没有空闲的线程处理任务,并且等待队列已满(当然这只对有界队列有效),再有新任务进来的话,就要做一些取舍了,而这个参数就是指定取舍策略的,有下面四种策略可以选择:

    ThreadPoolExecutor.AbortPolicy:直接抛出异常 RejectedExecutionException ,这是默认策略; 

    ThreadPoolExecutor.DiscardPolicy:直接丢弃任务,但是不抛出异常。 

    ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后将新来的任务加入等待队列

    ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务,并提供一种简单的反馈机制,可以有效防止新任务的提交。比如在 main 函数中提交线程,如果执行此策略,将有 main 线程来执行该任务

    ThreadPoolExecutor.AbortPolicy:直接抛出异常 RejectedExecutionException ,这是默认策略; Java 提供的4种默认实现的线程池都是使用的这种策略。

     

     

    线程池的相关问题

     

    线程池相关方法

       线程池也提供了一些相关的方法,大致如下:

    execute()

    submit()

    shutdown()

    shutdownNow()

    还有很多其他的方法:

      比如:getQueue() 、getPoolSize() 、getActiveCount()、getCompletedTaskCount()等获取与线程池相关属性的方法,有兴趣的朋友可以自行查阅API。

     

    execute()

      execute()方法实际上是Executor中声明的方法,在ThreadPoolExecutor进行了具体的实现,这个方法是ThreadPoolExecutor的核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行。

     

    submit()

       submit()方法是在ExecutorService中声明的方法,在AbstractExecutorService就已经有了具体的实现,在ThreadPoolExecutor中并没有对其进行重写,这个方法也是用来向线程池提交任务的,但是它和execute()方法不同,它能够返回任务执行的结果,去看submit()方法的实现,会发现它实际上还是调用的execute()方法,只不过它利用了Future来获取任务执行结果(Future相关内容将在下一篇讲述)。
     

    shutdown()

      shutdown() 提供一种有序的关机,会等待当前缓存队列任务全部执行完成才会关闭,但不会再接收新的任务(相对较优雅)。

     

    shutdownNow()

      shutdownNow() 会立即关闭线程池,会打断正在执行的任务并且会清空缓存队列中的任务,返回的是尚未执行的任务。

     

     

     

    corePoolSize与maximumPoolSize关系

    1、池中线程数小于corePoolSize,新任务都不排队而是直接添加新线程

    2、池中线程数大于等于corePoolSize,workQueue未满,首选将新任务加入workQueue而不是添加新线程

    3、池中线程数大于等于corePoolSize,workQueue已满,但是线程数小于maximumPoolSize,添加新的线程来处理被添加的任务

    4、池中线程数大于大于corePoolSize,workQueue已满,并且线程数大于等于maximumPoolSize,新任务被拒绝,使用handler处理被拒绝的任务

     

     

    手动创建线程池

     

    下面演示下如何手动创建线程池:

    这里我们使用的 Guava 的 ThreadFactory, 相关的 pom

    <dependencies>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>14.0.1</version>
        </dependency>
    </dependencies>

     

    线程池创建代码

            ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("thread-call-runner-%d").build();
            ExecutorService taskExe = new ThreadPoolExecutor(1, 1, 200L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(1), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
           
     

    创建了1个线程池  coreSize 1, maxSize 1,  使用有界等待队列初始大小为1, 传递Guava 创建的线程工厂(主要是为了给线程命名),  拒绝策略为直接抛出异常

     

    测试代码 

    package thread.pool;
    
    import com.google.common.util.concurrent.ThreadFactoryBuilder;
    
    import java.util.concurrent.*;
    
    /**
     * Created by szh on 2020/6/8.
     */
    public class ThreadPoolManual {
    
        public static int i = 1;
    
        public static volatile boolean flag = false;
    
        public static void main(String[] args) {
    
            ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("thread-call-runner-%d").build();
            ExecutorService taskExe = new ThreadPoolExecutor(1, 1, 200L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(1), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
            taskExe.submit(new Thread(() -> {
                while (ThreadPoolManual.i <= 99) {
                    if (ThreadPoolManual.flag == false) {
                        System.out.println(Thread.currentThread().getName() + " " + i);
                        ThreadPoolManual.i++;
                        ThreadPoolManual.flag = true;
                    }
                }
    
            }));
    
    
            taskExe.submit(new Thread(() -> {
                while (ThreadPoolManual.i <= 100) {
                    if (ThreadPoolManual.flag == true) {
                        System.out.println(Thread.currentThread().getName() + " " + i);
                        ThreadPoolManual.i++;
                        ThreadPoolManual.flag = false;
                    }
                }
            }));
    
            taskExe.submit(new Runnable() {
                               @Override
                               public void run() {
                                   System.out.println("xxxxx");
                               }
                           }
            );
        }
    
    }
    

     

    分析

    总共3个线程,2个线程 交替打印 0~ 100, 格外并提交了1个线程用来干扰,

    因为线程池当前运行一个线程1,另一个线程处于等待队列,第3个线程触发了拒绝策略。

    输出

    Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@5b480cf9 rejected from java.util.concurrent.ThreadPoolExecutor@6f496d9f[Running, pool size = 1, active threads = 1, queued tasks = 1, completed tasks = 0]
    	at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)
    	at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823)
    	at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369)
    	at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
    	at thread.pool.ThreadPoolManual.main(ThreadPoolManual.java:42)
    thread-call-runner-0 1
    

     

    展开全文
  • 创建线程池的几种方式&& 核心参数详解概述Executor及常见子类UML图核心参数详解int corePoolSizeint maximumPoolSizelong keepAliveTimeTimeUnit unitBlockingQueue workQueueThreadFactory ...

    概述

    通过线程池来管理线程的方式,优势十分明显:

    • 线程生命周期的开销非常高(创建与销毁)
    • 线程运行占用系统资源(当活跃线程数大于系统CPU数量时,大量处于就绪状态的线程消耗了系统资源)
    • 通过集中管理线程,确保系统稳定性(线程创建本身需要占用系统资源,大量创建新线程可能导致OOM )

    综合以上几点,一般在生产环境严禁不通过线程池的方式来创建线程;

    Executor及常见子类UML图

    在这里插入图片描述

    核心参数详解

    int corePoolSize

    • 线程池中活跃的线程数目

    int maximumPoolSize

    • 线程池中最大的线程数目

    long keepAliveTime

    • 线程最大空闲时间

    TimeUnit unit

    • keepAliveTime的单位
      • TimeUnit.NANOSECONDS 纳秒
      • TimeUnit.MICROSECONDS 微秒
      • TimeUnit.MILLISECONDS 毫秒
      • TimeUnit.SECONDS 秒
      • TimeUnit.MINUTES
      • TimeUnit.HOURS
      • TimeUnit.DAYS

    BlockingQueue workQueue

    • 当通过execute方法提交的任务,在被执行前的临时保存队列

    ThreadFactory threadFactory

    • 执行器创建一个新线程的工厂类

    RejectedExecutionHandler handler

    • 当前工作队列已满时,新任务提交的拒绝策略
      • CallerRunsPolicy:将任务交由当前提交的线程执行
      • AbortPolicy:通过抛异常的方式,拒绝提交
      • DiscardPolicy:不抛异常,拒绝提交
      • DiscardOldestPolicy:抛弃最开始提交的(队列头部)的一条任务,将此任务压入队尾

    常用创建线程池方式

    通过构造器,自定义线程池

    	ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                    10,
                    20,
                    0,
                    TimeUnit.SECONDS,
                    new LinkedBlockingDeque<>(50),
                    new ThreadPoolExecutor.DiscardOldestPolicy());
    

    Executors工具类创建线程池

    public class MyExecutor {
        public static void main(String[] args) {
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                    10,
                    20,
                    0,
                    TimeUnit.SECONDS,
                    new LinkedBlockingDeque<>(50),
                    new ThreadPoolExecutor.DiscardOldestPolicy()
            );
    
            // 创建只有一个线程的线程池
            ExecutorService executorService1 = Executors.newSingleThreadExecutor();
    
            // 创建一个无限大的线程池,
            // 当有新任务到达时,一定需要一个线程来执行
            // 适用于线程执行时间短的场景
            ExecutorService executorService2 = Executors.newCachedThreadPool();
    
            // 创建固定线程数量的线程池
            // corePoolSize = maximunPoolSize
            ExecutorService executorService3 = Executors.newFixedThreadPool(20);
    
            // 创建需要任务延迟执行的线程池
            ExecutorService executorService4 = Executors.newScheduledThreadPool(20);
        }
    }
    
    展开全文
  • 线程池核心参数介绍

    千次阅读 2018-12-01 11:27:08
    前言 我们创建线程的常见方式一般有继承Thread类以及实现Runnable接口,...通过创建线程池就可以解决这个问题。 通过线程池创建的线程执行完毕之后并不会销毁,而是会回到线程池继续重复利用,执行其他任务。这里简...

    前言
    我们创建线程的常见方式一般有继承Thread类以及实现Runnable接口,其实Thread类也是实现了Runnable接口。通过这两种方式创建的线程,在执行完毕之后都会被销毁,这样频繁的创建和销毁线程是一件很浪费资源到的事情。那么,有没有什么办法解决这个问题呢?通过创建线程池就可以解决这个问题。

    通过线程池创建的线程执行完毕之后并不会销毁,而是会回到线程池继续重复利用,执行其他任务。这里简单介绍一下线程池的几个主要参数:

    一、核心参数

    1. corePoolSize(核心线程数)
      (1)核心线程会一直存在,即使没有任务执行;
      (2)当线程数小于核心线程数的时候,即使有空闲线程,也会一直创建线程直到达到核心线程数;
      (3)设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭。
    2. queueCapacity(任务队列容量)
      也叫阻塞队列,当核心线程都在运行,此时再有任务进来,会进入任务队列,排队等待线程执行。
    3. maxPoolSize(最大线程数)
      (1)线程池里允许存在的最大线程数量;
      (2)当任务队列已满,且线程数量大于等于核心线程数时,会创建新的线程执行任务;
      (3)线程池里允许存在的最大线程数量。当任务队列已满,且线程数量大于等于核心线程数时,会创建新的线程执行任务。
    4. keepAliveTime(线程空闲时间)
      (1)当线程空闲时间达到keepAliveTime时,线程会退出(关闭),直到线程数等于核心线程数;
      (2)如果设置了allowCoreThreadTimeout=true,则线程会退出直到线程数等于零。
    5. allowCoreThreadTimeout(允许核心线程超时)
    6. rejectedExecutionHandler(任务拒绝处理器)
      (1)当线程数量达到最大线程数,且任务队列已满时,会拒绝任务;
      (2)调用线程池shutdown()方法后,会等待执行完线程池的任务之后,再shutdown()。如果在调用了shutdown()方法和线程池真正shutdown()之间提交任务,会拒绝新任务。

    二、线程池参数默认值

    • corePoolSize = 1
    • queueCapacity = Integer.MAX_VALUE
    • maxPoolSize = Integer.MAX_VALUE
    • keepAliveTime = 60秒
    • allowCoreThreadTimeout = false
    • rejectedExecutionHandler = AbortPolicy()

    三、ThreadPoolExecutor(线程池)执行顺序

    • 当线程数小于核心线程数时,会一直创建线程直到线程数等于核心线程数;
    • 当线程数等于核心线程数时,新加入的任务会被放到任务队列等待执行;
    • 当任务队列已满,又有新的任务时,会创建线程直到线程数量等于最大线程数;
    • 当线程数等于最大线程数,且任务队列已满时,新加入任务会被拒绝。
    展开全文
  • 2.5.3 线程池核心参数 默认参数: corePoolSize = 1 queueCapacity = Integer.MAX_VALUE maxPoolSize = Integer.MAX_VALUE keepAliveTime = 60秒 unit:秒,分钟 allowCoreThreadTimeout = false ThreadFactory...

    1. 什么是线程?线程和进程的区别?

    • 线程:是进程的一个实体,是 cpu 调度和分派的基本单位,是比进程更小的可以独立运 行的基本单位

    • 进程:具有一定独立功能的程序关于某个数据集合上的一次运行活动,是操作系统进行资源分配和调度的一个独立单位。

      特点:线程的划分尺度小于进程,这使多线程程序拥有高并发性,进程在运行时各自内存 单元相互独立,线程之间内存共享,这使多线程编程可以拥有更好的性能和用户体验
      注意:多线程编程对于其它程序是不友好的,占据大量 cpu 资源。

    2. 创建线程的4种方式

    继承Thread类,实现Runnable接口,实现Callable接口,线程池,共4中方法.

    2.1 继承Thread

    Thread 类本质上是实现了 Runnable 接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread 类的 start()实例方法。start()方法是一个 native 方法,它将启动一个新线程,并执行 run()方法

    package com.acece.ThreadReview;
    
    /**
     * @author 啊策策
     */
    //继承Thread类,创建线程
    public class CreateThreadFirst extends Thread{
        public void run(){
            System.out.println("CreateThreadFirst.run()");
        }
    
    }
    //测试线程
    class ThreadTest{
        public static void main(String[] args) {
            CreateThreadFirst createThreadFirst = new CreateThreadFirst();
            createThreadFirst.start();
        }
    }
    

    2.2 实现Runnable接口

    • 好处是,接口可以多实现,而继承Thread只能单继承
    • 本质上也是调用run()方法.
    package com.acece.ThreadReview;
    /**
     * @author 啊策策
     */
    public class CreateThreadSecond implements Runnable{
        public void run() {
            System.out.println("CreateThreadSecond.run()...");
        }
    }
    
    //测试线程
    class ThreadTest{
        public static void main(String[] args) {
            //2. 测试第二种方法
            CreateThreadSecond createThreadSecond = new CreateThreadSecond();
            Thread thread = new Thread(createThreadSecond);
            thread.start();
        }
    }
    

    2.3 实现Callable接口

    • Java 5.0 在 java.util.concurrent 提供了一个新的创建执行线程的方式:Callable 接口
    • 相对于Runnable接口,Callable接口有返回值,而且也可以抛出经检测的异常.
    • Callable 需要依赖FutureTask ,FutureTask 也可以用作闭锁。

    具体步骤如下

    1. 实现Callable接口,实现其中的call方法。
    2. 新建实现Callable接口的对象。
    3. 新建FutureTask对象,其中参数是Callable接口对应的对象。
    4. 新建Thread对象,其中参数是FutureTask对象。
    5. 执行线程:Thread对象.start方法。
    6. FutureTask对象.get方法等待返回值。
    7. 在线程结果没有被get到的时候,它会阻塞后面的内容,也可以用于闭锁。
    package com.acece.ThreadReview;
    
    import java.util.concurrent.*;
    
    /**
     * @author 啊策策
     */
    //1. 创建一个类实现Callable接口,实现call()方法,
    public class CreateThreadFourth implements Callable<Integer>{
    
        //新增返回值(接口的泛型上体现),新增了可抛出异常
        public Integer call() throws Exception {
            int sum = 0;
            for (int i = 0; i < 10; i++) {
                sum += i;
                System.out.println(i);
            }
            return sum;
        }
    }
    
    //创建一个类,测试CreateThreadThird
    class CreateThreadThirdTest{
        public static void main(String[] args) {
            long sum = 0;
    
            //2. 新建实现Callable接口类的对象
            CreateThreadThird createThreadThird = new CreateThreadThird();
    
            //3. 新建FutureTask对象,其中参数是Callable接口对应的对象.
            FutureTask<Integer> task = new FutureTask<Integer>(createThreadThird);
    
            //4. 新建Thread对象,其中参数是FutureTask对象.
            //5. 执行线程: Thread对象.start方法
            new Thread(task).start();
    
            //6.FutureTask对象.get方法等待返回值.
            try {
                sum = task.get();
            } catch (Exception e) {
                e.printStackTrace();
            }
            //7.在线程结果没有被get到的时候,它会阻塞后面的内容,也可以用于闭锁.
            System.out.println("总和是: " + sum);
        }
    }
    

    2.4 通过线程池创建线程

    线程池就是事先将多个线程对象放到一个容器中,当使用的时候就不用 new 线程而是直接去池中拿线程即可,节省了开辟子线程的时间,提高的代码执行效率。 在 JDK 的 java.util.concurrent.Executors 中提供了生成多种线程池的静态方法.
    在这里插入图片描述
    然后再调用线城池的 execute 方法, 传递new Runnable()参数重写run()方法即可.

    2.4.1 常见的线程池有几种?

    在这里插入图片描述

    • newCachedThreadPool:创建一个可缓存的线程池,此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说 JVM)能够创建的最大线程大小。
    • newFixedThreadPool创建固定大小的线程池,每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。
    • newSingleThreadExecutor创建一个单线程的线程池,此线程池保证所有任务的执行顺序 按照任务的提交顺序执行。
    • newSingleThreadScheduleExecutor: 创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期执行.
    • newScheduledThreadPool:创建一个线程池,它可安排在给定延迟后运行命令或者定期的执行, 线城池数量不固定.
    • newWorkStealingPool: 创建一个带并行级别的线程池,并行级别决定了同一时刻最多有多少个线程在执行,如不传并行级别参数,将默认为当前系统的CPU个数.

    2.5.2 线城池执行原理

    在这里插入图片描述

    • 用户提交任务
    • 先判断当前线程数是否大于核心线程数
      • 如果没有大于, 则直接利用核心线程执行任务
      • 如果大于, 则可是往阻塞队列中放入任务.
        • 如果阻塞队列没放满, 则随着核心线程完成老的任务之后, 就完成队列中的任务了.
        • 如果阻塞队列放满了. 开始创建新线程执行任务
          • 如果, 新线程数量+核心线程数量小于等于最大线程数, 利用新线程和核心线程分摊任务.
          • 如果, 新线程数量+核心线程数量大于最大线程数, 则报错, 执行拒绝策略.

    2.5.3 线程池核心参数

    默认参数:

    	corePoolSize = 1
    	queueCapacity = Integer.MAX_VALUE
    	maxPoolSize = Integer.MAX_VALUE
    	keepAliveTime = 60秒
    	unit:秒,分钟
    	allowCoreThreadTimeout = false
        ThreadFactory=Executors.defaultThreadFactory
    	rejectedExecutionHandler = AbortPolicy()
    

    具体讲解:

    1. corePoolSize(核心线程数)
      (1)核心线程会一直存在,即使没有任务执行;
      (2)当线程数小于核心线程数的时候,即使有空闲线程,也会一直创建线程直到达到核心线程数;
      (3)核心线程数代表我能够维护常用的线程开销.
    2. maxPoolSize(最大线程数)
      (1)线程池里允许存在的最大线程数量;
      (2)线程池里允许存在的最大线程数量。当任务队列已满,且线程数量大于等于核心线程数时,会创建新的线程执行任务。
    3. queueCapacity(阻塞队列)
      当核心线程都在运行,此时再有任务进来,会进入任务队列,排队等待线程执行。
    4. keepAliveTime(线程空闲时间)
      (1)当线程空闲时间达到keepAliveTime时,线程会退出(关闭).
      (2)如果allowCoreThreadTimeout=true,则线程会继续退出直到为0个。
      (3)默认allowCoreThreadTimeout=false, 也就是说线程池中的线程退出, 直到=核心线程数.
    5. unit: 和keepAliveTime配合使用, 时间单位, 可以为秒, 分钟.
    6. allowCoreThreadTimeout(允许核心线程超时, 默认为false, jdk1.6后加的新特性)
    7. ThreadFactory:
      线程创建的工厂,新的线程都是由ThreadFactory创建的,系统默认使用的是
      Executors.defaultThreadFactory创建的,用它创建出来的线程的优先级、组等都是一样
      的,并且他都不是守护线程。我们也可以使用自定义的线程创建工厂,并对相关的值进
      行修改
    8. rejectedExecutionHandler(任务拒绝处理器)
      (1)当线程数量达到最大线程数,且任务队列已满时,会拒绝任务;
      (2)调用线程池shutdown()方法后,会等待执行完线程池的任务之后,再shutdown()。如果在调用了shutdown()方法和线程池真正shutdown()之间提交任务,会拒绝新任务。

    2.5.4 拒绝策略

    线程池中的线程已经用完了,无法继续为新任务服务,同时,等待队列也已经排满了, 再也塞不下新任务了。这时候我们就需要拒绝策略机制合理的处理这个问题。

    • ThreadPoolExecutor.AbortPolicy(系统默认): 丢弃任务并抛出RejectedExecutionException异常,让你感知到任务被拒绝了,我们可以根据业务逻辑选择重试或者放弃提交等策略
    • ThreadPoolExecutor.DiscardPolicy: 也是丢弃任务,但是不抛出异常,相对而言存在一定的风险,因为我们提交的时候根本不知道这个任务会被丢弃,可能造成数据丢失。
    • ThreadPoolExecutor.DiscardOldestPolicy: 丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程),通常是存活时间最长的任务,它也存在一定的数据丢失风险
    • ThreadPoolExecutor.CallerRunsPolicy: 由调用线程处理该任务

    2.5.5 使用线程池的好处?

    第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
    第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
    第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源, 还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控.

    2.5.6 如果关闭线城池

    • 刚才提过线城池核心参数, 设置时间, 会自动关闭
    • shutdownNow():线程池拒接收新提交的任务,同时立马关闭线程池,线程池里的任务不再执行.
    • showdown(): 线程池拒接收新提交的任务,同时等待线程池里的任务执行完毕后关闭线程池。
    展开全文
  • ThreadPoolExecutor线程池核心参数详解

    万次阅读 2019-06-26 16:34:43
    我们知道,受限于硬件、内存和性能,我们不可能无限制的创建任意数量的线程,因为每一台机器允许的最大线程是一个有界值。也就是说ThreadPoolExecutor管理的线程数量是有界的。线程池就是用这些有限个数的线程,去...
  • ThreadPoolExecutor是创建线程池核心类,它定义了一些构造函数用来创建线程池,如下是它的其中一个构造函数: public ThreadPoolExecutor ( int corePoolSize , int maximumPoolSize , long ...
  • 创建线程池参数

    2020-01-20 17:03:37
    动态创建线程池参数 /** *获取当前机器CPU数量 */ private static final int CPU = Runtime.getRuntime().availableProcessors(); /** *核心线程数(默认线程数) */ private static final int CORE_POOL_SIZE =...
  • Executors是创建线程池的工厂类。提供了以下几个主要方法   public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, ...
  • 3、核心参数 4、工作原理 线程池绝对的面试高频,确实因为多线程是解决并发问题特别是提升某些核心项目接口的利器,但是使用不好也存在大量的问题,那么搞清楚线程池的工作原理尤为重要。之前接触线程池基本都...
  • 多线程---线程池核心参数

    千次阅读 2019-07-22 09:15:58
    关于Java线程池参数设置: 线程池是Java多线程里开发里的重要内容,使用难度不大,但如何用好就要明白参数的含义和如何去设置。干货里的内容大多是参考别人的,加入了一些知识点的扩充和看法。希望能对多线程开发...
  • Executors是创建线程池的工厂类。提供了以下几个主要方法 public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new ...
  • 下面我来解释一下这7个参数的用途: corePoolSize 线程池核心线程数量,核心线程不会被回收,即使没有任务执行,也会保持空闲状态。 maximumPoolSize 池允许最大的线程数,当线程数量达到corePoolSize,且workQueue...
  • ThreadPoolExecutor 初始化时,主要有如下几个参数: public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<
  • } } 五、为什么阿里巴巴不允许使用Executors 在阿里巴巴的Java开发手册中有一条如下: 避免使用Executors创建线程池,那么我们可以自己直接调用ThreadPoolExecutor的构造函数来自己创建线程池。在创建的同时,给...
  • 怎么创建线程池? Java通过Executors提供公司中线程池,分别为: CachThreadPool 创建一个可缓存的线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 FixedThreadPool 创建一个定...
  • 创建线程池每个参数有什么作用

    千次阅读 2019-06-28 10:44:46
    默认线程数量(核心线程数量),在创建线程池之后,线程池里没有任何线程,等到有任务进来时才创建线程去执行任务(懒加载)。当线程池中的线程数达到corePoolSize的值后,就会把到达的任务放到缓存队列里; ...
  • 总结线程池创建的方式,详解线程池核心参数意义并分析ThreadPoolExecutor源码
  • 线程池核心参数

    2019-10-15 14:37:42
    当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理(核心线程会一直存活,即使没有任务需要执行) 设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭 QueueCapacity:...
  • 用ThreadPoolExecutor手动创建线程池

    万次阅读 2020-04-04 18:30:59
    用ThreadPoolExecutor手动创建线程池 首先我们看一下ThreadPoolExecutor的构造方法 public ThreadPoolExecutor(int corePoolSize, // 核心线程池大小 int maximumPoolSize, // 最大线程池大小 lon...
  • 线程池核心参数

    2019-08-03 23:59:00
    1 为什么需要线程池 一般来说,线程的生命周期需要通过 new(新建)->start(就绪)->...使用线程池创建一定数量的线程,专门执行任务的run 方法,实现了线程的复用,同时也减少了其他状态的占比,提...
  • 线程池的ThreadPoolExecutor实现了Executor接口: 参数最全的构造方法如下: public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue workQueue,Thread...
  • 线程池由浅入深-手动创建线程池

    千次阅读 2018-07-23 22:26:56
     JDK提供了一个ThreadPoolExecutor类供我们来手动创建线程池,类定义如下:... 想要通过ThreadPoolExecutor类来创建线程池,实质上就是向其构造方法传入不同参数来获取不同的线程池。  看具体的构造方法之前,我...
  • 线程池ThreadPoolExecutor核心参数

    千次阅读 2019-02-18 14:23:44
    public ThreadPoolExecutor(  int corePoolSize, //核心池的大小。  int maximumPoolSize, //池中允许的最大线程数,这个参数表示了线程池中最多能创建的线程数量 ...
  • 1、newFixedThreadPool创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。 2、newCachedThreadPool创建一个可缓存的...
  • 文章目录线程池创建线程池(5种)线程池的主要参数三种阻塞队列四种拒绝策略线程执行流程 线程池 创建线程和销毁线程的代价是非常高的,而使用线程池就可以很好的提高性能,尤其是程序中需要创建大量生存期很短的...
  • 1.问什么需要线程池 考虑下面场景:加入我们通过restful方式对接一个系统A。每次想要调用A的服务的时候都会创建一个线程发起一个请求。当获得回应后,关闭该线程。 上面的逻辑没有问题。但是当我们需要频繁调用系统A...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 67,849
精华内容 27,139
关键字:

创建线程池的核心参数