精华内容
下载资源
问答
  • 线程池—Executors 详解

    2021-03-05 11:09:15
    各位志同道合的朋友们大家好,我是一个一直在一线互联网踩坑十余年的编码爱好者,...ThreadPoolExecutor 和 Executors,上一节学习了 ThreadPoolExecutor 的使用方式,本节重点来看 Executors 是如何创建线程池的...

    各位志同道合的朋友们大家好,我是一个一直在一线互联网踩坑十余年的编码爱好者,现在将我们的各种经验以及架构实战分享出来,如果大家喜欢,就关注我,一起将技术学深学透,我会每一篇分享结束都会预告下一专题

    线程池的创建分为两种方式:ThreadPoolExecutor 和 Executors,上一节学习了 ThreadPoolExecutor 的使用方式,本节重点来看 Executors 是如何创建线程池的。Executors 可以创建以下六种线程池。FixedThreadPool(n):创建一个数量固定的线程池,超出的任务会在队列中等待空闲的线程,可用于控制程序的最大并发数。

    CachedThreadPool():短时间内处理大量工作的线程池,会根据任务数量产生对应的线程,并试图缓存线程以便重复使用,如果限制 60 秒没被使用,则会被移除缓存。

    SingleThreadExecutor():创建一个单线程线程池。

    ScheduledThreadPool(n):创建一个数量固定的线程池,支持执行定时性或周期性任务。

    SingleThreadScheduledExecutor():此线程池就是单线程的 newScheduledThreadPool。

    WorkStealingPool(n):Java 8 新增创建线程池的方法,创建时如果不设置任何参数,则以当前机器处理器个数作为线程个数,此线程池会并行处理任务,不能保证执行顺序。

    下面分别来看以上六种线程池的具体代码使用。

    FixedThreadPool 使用

    创建固定个数的线程池,具体示例如下:

    ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);

    for (int i = 0; i < 3; i++) {

    fixedThreadPool.execute(() -> {

    System.out.println("CurrentTime - " + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

    try {

    Thread.sleep(1000);

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    });

    }

    以上程序执行结果如下:CurrentTime - 2019-06-27 20:58:58

    CurrentTime - 2019-06-27 20:58:58

    CurrentTime - 2019-06-27 20:58:59

    根据执行结果可以看出,newFixedThreadPool(2) 确实是创建了两个线程,在执行了一轮(2 次)之后,停了一秒,有了空闲线程,才执行第三次。

    CachedThreadPool 使用

    根据实际需要自动创建带缓存功能的线程池,具体代码如下:

    ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

    for (int i = 0; i < 10; i++) {

    cachedThreadPool.execute(() -> {

    System.out.println("CurrentTime - " +

    LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

    try {

    Thread.sleep(1000);

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    });

    }

    以上程序执行结果如下:CurrentTime - 2019-06-27 21:24:46

    CurrentTime - 2019-06-27 21:24:46

    CurrentTime - 2019-06-27 21:24:46

    CurrentTime - 2019-06-27 21:24:46

    CurrentTime - 2019-06-27 21:24:46

    CurrentTime - 2019-06-27 21:24:46

    CurrentTime - 2019-06-27 21:24:46

    CurrentTime - 2019-06-27 21:24:46

    CurrentTime - 2019-06-27 21:24:46

    CurrentTime - 2019-06-27 21:24:46

    根据执行结果可以看出,newCachedThreadPool 在短时间内会创建多个线程来处理对应的任务,并试图把它们进行缓存以便重复使用。

    SingleThreadExecutor 使用

    创建单个线程的线程池,具体代码如下:

    ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

    for (int i = 0; i < 3; i++) {

    singleThreadExecutor.execute(() -> {

    System.out.println("CurrentTime - " +

    LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));

    try {

    Thread.sleep(1000);

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    });

    }

    以上程序执行结果如下:CurrentTime - 2019-06-27 21:43:34

    CurrentTime - 2019-06-27 21:43:35

    CurrentTime - 2019-06-27 21:43:36

    ScheduledThreadPool 使用

    创建一个可以执行周期性任务的线程池,具体代码如下:

    ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(2);

    scheduledThreadPool.schedule(() -> {

    System.out.println("ThreadPool:" + LocalDateTime.now());

    }, 1L, TimeUnit.SECONDS);

    System.out.println("CurrentTime:" + LocalDateTime.now());

    以上程序执行结果如下:CurrentTime:2019-06-27T21:54:21.881

    ThreadPool:2019-06-27T21:54:22.845

    根据执行结果可以看出,我们设置的 1 秒后执行的任务生效了。

    SingleThreadScheduledExecutor 使用

    创建一个可以执行周期性任务的单线程池,具体代码如下:

    ScheduledExecutorService singleThreadScheduledExecutor = Executors.newSingleThreadScheduledExecutor();

    singleThreadScheduledExecutor.schedule(() -> {

    System.out.println("ThreadPool:" + LocalDateTime.now());

    }, 1L, TimeUnit.SECONDS);

    System.out.println("CurrentTime:" + LocalDateTime.now());

    WorkStealingPool 使用

    Java 8 新增的创建线程池的方式,可根据当前电脑 CPU 处理器数量生成相应个数的线程池,使用代码如下:

    ExecutorService workStealingPool = Executors.newWorkStealingPool();

    for (int i = 0; i < 5; i++) {

    int finalNumber = i;

    workStealingPool.execute(() -> {

    System.out.println("I:" + finalNumber);

    });

    }

    Thread.sleep(5000);

    以上程序执行结果如下:I:0

    I:3

    I:2

    I:1

    I:4

    根据执行结果可以看出,newWorkStealingPool 是并行处理任务的,并不能保证执行顺序。

    ThreadPoolExecutor VS Executors

    ThreadPoolExecutor 和 Executors 都是用来创建线程池的,其中 ThreadPoolExecutor 创建线程池的方式相对传统,而 Executors 提供了更多的线程池类型(6 种),但很不幸的消息是在实际开发中并不推荐使用 Executors 的方式来创建线程池。

    无独有偶《阿里巴巴 Java 开发手册》中对于线程池的创建也是这样规定的,内容如下:线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的读者更加明确线程池的运行规则,规避资源耗尽的风险。

    说明:Executors 返回的线程池对象的弊端如下:

    1)FixedThreadPool 和 SingleThreadPool:

    允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。

    2)CachedThreadPool 和 ScheduledThreadPool:

    允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

    OOM 是 OutOfMemoryError 的缩写,指内存溢出的意思。

    为什么不允许使用 Executors?

    我们先来看一个简单的例子:

    ExecutorService maxFixedThreadPool = Executors.newFixedThreadPool(10);

    for (int i = 0; i < Integer.MAX_VALUE; i++) {

    maxFixedThreadPool.execute(()->{

    try {

    Thread.sleep(1000);

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    });

    }

    之后设置 JVM(Java 虚拟机)的启动参数: -Xmx10m -Xms10m (设置 JVM 最大运行内存等于 10M)运行程序,会抛出 OOM 异常,信息如下:Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded

    at java.util.concurrent.LinkedBlockingQueue.offer(LinkedBlockingQueue.java:416)

    at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1371)

    at xxx.main(xxx.java:127)

    为什么 Executors 会存在 OOM 的缺陷?

    通过以上代码,找到了 FixedThreadPool 的源码,代码如下:

    public static ExecutorService newFixedThreadPool(int nThreads) {

    return new ThreadPoolExecutor(nThreads, nThreads,

    0L, TimeUnit.MILLISECONDS,

    new LinkedBlockingQueue());

    }

    可以看到创建 FixedThreadPool 使用了 LinkedBlockingQueue 作为任务队列,继续查看 LinkedBlockingQueue 的源码就会发现问题的根源,源码如下:

    public LinkedBlockingQueue() {

    this(Integer.MAX_VALUE);

    }

    当使用 LinkedBlockingQueue 并没有给它指定长度的时候,默认长度为 Integer.MAX_VALUE,这样就会导致程序会给线程池队列添加超多个任务,因为任务量太大就有造成 OOM 的风险。

    相关面试题

    1.以下程序会输出什么结果?

    public static void main(String[] args) {

    ExecutorService workStealingPool = Executors.newWorkStealingPool();

    for (int i = 0; i < 5; i++) {

    int finalNumber = i;

    workStealingPool.execute(() -> {

    System.out.print(finalNumber);

    });

    }

    }

    A:不输出任何结果B:输出 0 到 9 有序数字C:输出 0 到 9 无需数字D:以上全对

    答:A题目解析:newWorkStealingPool 内部实现是 ForkJoinPool,它会随着主程序的退出而退出,因为主程序没有任何休眠和等待操作,程序会一闪而过,不会执行任何信息,所以也就不会输出任何结果。

    2.Executors 能创建单线程的线程池吗?怎么创建?

    答:Executors 可以创建单线程线程池,创建分为两种方式:Executors.newSingleThreadExecutor():创建一个单线程线程池。

    Executors.newSingleThreadScheduledExecutor():创建一个可以执行周期性任务的单线程池。

    3.Executors 中哪个线程适合执行短时间内大量任务?

    答:newCachedThreadPool() 适合处理大量短时间工作任务。它会试图缓存线程并重用,如果没有缓存任务就会新创建任务,如果线程的限制时间超过六十秒,则会被移除线程池,因此它比较适合短时间内处理大量任务。

    4.可以执行周期性任务的线程池都有哪些?

    答:可执行周期性任务的线程池有两个,分别是:newScheduledThreadPool() 和 newSingleThreadScheduledExecutor(),其中 newSingleThreadScheduledExecutor() 是 newScheduledThreadPool() 的单线程版本。

    5.JDK 8 新增了什么线程池?有什么特点?

    答:JDK 8 新增的线程池是 newWorkStealingPool(n),如果不指定并发数(也就是不指定 n),newWorkStealingPool() 会根据当前 CPU 处理器数量生成相应个数的线程池。它的特点是并行处理任务的,不能保证任务的执行顺序。

    6.newFixedThreadPool 和 ThreadPoolExecutor 有什么关系?

    答:newFixedThreadPool 是 ThreadPoolExecutor 包装,newFixedThreadPool 底层也是通过 ThreadPoolExecutor 实现的。

    newFixedThreadPool 的实现源码如下:

    public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {

    return new ThreadPoolExecutor(nThreads, nThreads,

    0L, TimeUnit.MILLISECONDS,

    new LinkedBlockingQueue(),

    threadFactory);

    }

    7.单线程的线程池存在的意义是什么?

    答:单线程线程池提供了队列功能,如果有多个任务会排队执行,可以保证任务执行的顺序性。单线程线程池也可以重复利用已有线程,减低系统创建和销毁线程的性能开销。

    8.线程池为什么建议使用 ThreadPoolExecutor 创建,而非 Executors?

    答:使用 ThreadPoolExecutor 能让开发者更加明确线程池的运行规则,避免资源耗尽的风险。

    Executors 返回线程池的缺点如下:FixedThreadPool 和 SingleThreadPool 允许请求队列长度为 Integer.MAX_VALUE,可能会堆积大量请求,可能会导致内存溢出;

    CachedThreadPool 和 ScheduledThreadPool 允许创建线程数量为 Integer.MAX_VALUE,创建大量线程,可能会导致内存溢出。

    总结

    Executors 可以创建 6 种不同类型的线程池,其中 newFixedThreadPool() 适合执行单位时间内固定的任务数,newCachedThreadPool() 适合短时间内处理大量任务,newSingleThreadExecutor() 和 newSingleThreadScheduledExecutor() 为单线程线程池,而 newSingleThreadScheduledExecutor() 可以执行周期性的任务,是 newScheduledThreadPool(n) 的单线程版本,而 newWorkStealingPool() 为 JDK 8 新增的并发线程池,可以根据当前电脑的 CPU 处理数量生成对比数量的线程池,但它的执行为并发执行不能保证任务的执行顺序。

    下一篇:ThreadLocal详解在公众号菜单中可自行获取专属架构视频资料,包括不限于 java架构、python系列、人工智能系列、架构系列,以及最新面试、小程序、大前端均无私奉献,你会感谢我的哈

    往期精选

    展开全文
  • Executors线程池

    2021-04-19 08:02:08
    要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在Executors类里面提供了一些静态工厂,生成一些常用的线程池。 newSingleThreadExecutor 说明:...

    简介

    线程Thread是一个重量级资源,线程的创建、启动以及销毁都是比较耗费系统资源的,同时受限于系统资源的限制,线程的数量与系统性能是一种抛物线的关系,因此对线程的管理,是一种非常好的程序设计习惯,自JDK1.5起,utils包提供了ExecutorService[ɪɡˈzɛkjətɚ]线程池的实现。通俗的将:为了避免重复的创建线程,线程池的出现可以让线程进行复用。当有工作来,就会向线程池拿一个线程,当工作完成后,并不是直接关闭线程,而是将这个线程归还给线程池供其他任务使用。
    一个线程池包括以下四个基本组成部分:

    • 1、线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;
    • 2、工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
    • 3、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
    • 4、任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。

    线程池的作用

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

     

    线程池的创建

    Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在Executors类里面提供了一些静态工厂,生成一些常用的线程池。

    1. newSingleThreadExecutor
      说明:初始化只有一个线程的线程池,内部使用LinkedBlockingQueue作为阻塞队列。
      特点:相当于单线程串行执行所有任务如果该线程异常结束,会重新创建一个新的线程继续执行任务,唯一的线程可以保证所提交任务的顺序执行
    2. newFixedThreadPool
      说明:初始化一个指定线程数的线程池,其中corePoolSize == maxiPoolSize,使用LinkedBlockingQuene作为阻塞队列
      特点:每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,即使当线程池没有可执行任务时,也不会释放线程。如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
    3. newCachedThreadPool
      说明:初始化一个可以缓存线程的线程池,此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。线程池的线程数可达到Integer.MAX_VALUE,即2147483647,内部使用SynchronousQueue作为阻塞队列;
      特点:在没有任务执行时,当线程的空闲时间超过keepAliveTime,默认为60s,会自动释放线程资源;当提交新任务时,如果没有空闲线程,则创建新线程执行任务,会导致一定的系统开销;
      因此,使用时要注意控制并发的任务数,防止因创建大量的线程导致而降低性能。
    4. newScheduledThreadPool
      特定:初始化的线程池可以在指定的时间内周期性的执行所提交的任务,在实际的业务场景中可以使用该线程池定期的同步数据。
      总结:除了newScheduledThreadPool的内部实现特殊一点之外,其它线程池内部都是基于ThreadPoolExecutor类(Executor的子类)实现的。

    线程池的状态

    其中AtomicInteger变量ctl的功能非常强大:利用低29位表示线程池中线程数,通过高3位表示线程池的运行状态:
    1、RUNNING:-1 << COUNT_BITS,即高3位为111,该状态的线程池会接收新任务,并处理阻塞队列中的任务;
    2、SHUTDOWN: 0 << COUNT_BITS,即高3位为000,该状态的线程池不会接收新任务,但会处理阻塞队列中的任务;
    3、STOP : 1 << COUNT_BITS,即高3位为001,该状态的线程不会接收新任务,也不会处理阻塞队列中的任务,而且会中断正在运行的任务;
    4、TIDYING : 2 << COUNT_BITS,即高3位为010,该状态表示线程池对线程进行整理优化;
    5、TERMINATED: 3 << COUNT_BITS,即高3位为011,该状态表示线程池停止工作;

    线程池的关闭

    1. ThreadPoolExecutor提供了两个方法,用于线程池的关闭,分别是shutdown()和shutdownNow(),其中:
      shutdown():不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务
      shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务

    应用实例

    首先创建一个自定义的线程,模拟并发任务。

    public class MyThread extends Thread {
        private int i;
        public MyThread(int in) {
            this.i = in;
        }
        public void run() {
            try {
                this.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(currentThread().getName()+"正在打印:"+i);
        }
    }
    
    • 1、newSingleThreadExecutor
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class MyThreadText {
        public static void main(String[] args) {
            ExecutorService executorService = Executors.newSingleThreadExecutor();
            for (int i = 0; i < 10; i++) {
                executorService.execute(new MyThread(i));
            }
            executorService.shutdown();
        }
    }
    
    • 1

    在这里插入图片描述

    2、newFixedThreadPool

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class MyThreadText {
        public static void main(String[] args) {
            ExecutorService executorService = Executors.newFixedThreadPool(5);
            for (int i = 0; i < 10; i++) {
                executorService.execute(new MyThread(i));
            }
            executorService.shutdown();
        }
    }
    
    •  

    在这里插入图片描述

    3、newCachedThreadPool

    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    public class MyThreadText {
        public static void main(String[] args) {
            ExecutorService executorService = Executors.newCachedThreadPool();
            for (int i = 0; i < 10; i++) {
                executorService.execute(new MyThread(i));
            }
            executorService.shutdown();
        }
    }
    
    •  

    在这里插入图片描述

    4、定时线程池(newScheduledThreadPool)

    package com.zhw.learning.thread;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    import java.util.concurrent.TimeUnit;
    
    /**
     * @author zhw
     * 创建一个可定期或者延时执行任务的定长线程池,支持定时及周期性任务执行
     *
     * Executors.newScheduledThreadPool(3):
     * public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
     *         return new ScheduledThreadPoolExecutor(corePoolSize);
     *     }
     * public ScheduledThreadPoolExecutor(int corePoolSize) {
     *         super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
     *               new DelayedWorkQueue());
     *     }
     */
    public class ScheduledThreadPoolTest {
    
        public static void main(String[] args) throws InterruptedException {
            final SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
    
            ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);
    
            System.out.println("提交时间: " + sdf.format(new Date()));
    
            //延迟3秒钟后执行任务
    //        scheduledThreadPool.schedule(new Runnable() {
    //            @Override
    //            public void run() {
    //                System.out.println("运行时间: " + sdf.format(new Date()));
    //            }
    //        }, 3, TimeUnit.SECONDS);
    
            //延迟1秒钟后每隔3秒执行一次任务
            scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
                    System.out.println("运行时间: " + sdf.format(new Date()));
                }
            }, 1, 3, TimeUnit.SECONDS);
            Thread.sleep(10000);
    
            scheduledThreadPool.shutdown();
        }
    
    }
    
    

    延迟3秒钟后执行任务,运行结果如下:
    在这里插入图片描述
    延迟1秒钟后每隔3秒执行一次任务,运行结果如下:


    ThreadPoolExecutor执行execute方法分下面4种情况。

    1)如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(注意,执行这一步骤 需要获取全局锁)。

    2)如果运行的线程等于或多于corePoolSize,则将任务加入BlockingQueue。 3)如果无法将任务加入BlockingQueue(队列已满),则创建新的线程来处理任务(注意,执行这一步骤需要获取全局锁)。 4)如果创建新线程将使当前运行的线程超出maximumPoolSize,任务将被拒绝,并调用 RejectedExecutionHandler.rejectedExecution()方法。 ThreadPoolExecutor采取上述步骤的总体设计思路,是为了在执行execute()方法时,尽可能 地避免获取全局锁(那将会是一个严重的可伸缩瓶颈)。在ThreadPoolExecutor完成预热之后 (当前运行的线程数大于等于corePoolSize),几乎所有的execute()方法调用都是执行步骤2,而 步骤2不需要获取全局锁。 

    2、线程池的主要参数

      假如有一个工厂,工厂里面有10个工人,每个工人同时只能做一件任务。因此只要当10个工人中有工人是空闲的,来了任务就分配给空闲的工人做;当10个工人都有任务在做时,如果还来了任务,就把任务进行排队等待;如果说新任务数目增长的速度远远大于工人做任务的速度,那么此时工厂主管可能会想补救措施,比如重新招4个临时工人进来;然后就将任务也分配给这4个临时工人做; 如果说着14个工人做任务的速度还是不够,此时工厂主管可能就要考虑不再接收新的任务或者抛弃前面的一些任务了。当这14个工人当中有人空闲时,而新任务增长的速度又比较缓慢,工厂主管可能就考虑辞掉4个临时工了,只保持原来的10个工人,毕竟请额外的工人是要花钱的。10个人是全职工人,4个人是零时工,全职工人和工作队列满的时候才会找临时工,零食工在一定时间内没工作那么就辞掉它

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

    1)corePoolSize(线程池的基本大小):当提交一个任务到线程池时,线程池会创建一个线 程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任 务数大于线程池基本大小时就不再创建。如果调用了线程池的prestartAllCoreThreads()方法, 线程池会提前创建并启动所有基本线程。

    • corePoolSize:核心池的大小,这个参数跟后面讲述的线程池的实现原理有非常大的关系。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;

    2)runnableTaskQueue(任务队列):用于保存等待执行的任务的阻塞队列。可以选择以下几 个阻塞队列。

    ·ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按FIFO(先进先出)原 则对元素进行排序。

    ·LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO排序元素,吞吐量通 常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。

    ·SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用 移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于Linked-BlockingQueue,静态工 厂方法Executors.newCachedThreadPool使用了这个队列。

    ·PriorityBlockingQueue:一个具有优先级的无限阻塞队列。

    3)maximumPoolSize(线程池最大数量):线程池允许创建的最大线程数。如果队列满了,并 且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是,如 果使用了无界的任务队列这个参数就没什么效果。

    4)ThreadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设 置更有意义的名字。使用开源框架guava提供的ThreadFactoryBuilder可以快速给线程池里的线 程设置有意义的名字,代码如下。

       new ThreadFactoryBuilder().setNameFormat("XX-task-%d").build();
    

    5)RejectedExecutionHandler(饱和策略):当队列和线程池都满了,说明线程池处于饱和状 态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是AbortPolicy,表示无法 处理新任务时抛出异常。在JDK 1.5中Java线程池框架提供了以下4种策略。

    ·AbortPolicy:直接抛出异常。

    ·CallerRunsPolicy:只用调用者所在线程来运行任务。

    ·DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)

    ,并执行当前任务。

    ·DiscardPolicy:不处理,丢弃掉。

    当然,也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略。如记录 日志或持久化存储不能处理的任务。

    6.·keepAliveTime(线程活动保持时间):线程池的工作线程空闲后,保持存活的时间。所以, 如果任务很多,并且每个任务执行的时间比较短,可以调大时间,提高线程的利用率。

    • keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;

    7.·TimeUnit(线程活动保持时间的单位):可选的单位有天(DAYS)、小时(HOURS)、分钟 (MINUTES)、毫秒(MILLISECONDS)、微秒(MICROSECONDS,千分之一毫秒)和纳秒(NANOSECONDS,千分之一微秒)。
     

    9.2.2 向线程池提交任务 可以使用两个方法向线程池提交任务,分别为execute()和submit()方法。

    execute() vs submit()

    • execute()方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否;
    • submit()方法用于提交需要返回值的任务。线程池会返回一个 Future 类型的对象,通过这个 Future 对象可以判断任务是否执行成功(可以通过 Future 的 get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,而使用 get(long timeout,TimeUnit unit)方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完。)

     

    在这里插入图片描述
    handler有四个选择:

    策略1:ThreadPoolExecutor.AbortPolicy()
    抛出java.util.concurrent.RejectedExecutionException异常 ,示例如下:
    在这里插入图片描述
    小疑问:笔者对运行结果还是有一些疑问,为什么异常在下面第3行就出现了
    在这里插入图片描述

    策略2:ThreadPoolExecutor.CallerRunsPolicy
    如果任务被拒绝了,则由调用线程(提交任务的线程)直接执行此任务,我们可以通过代码来验证这一点:
    在这里插入图片描述
    解释:上面zxai
    策略3:RejectedExecutionHandler handler =
    new ThreadPoolExecutor.DiscardOldestPolicy();
    这样运行结果就不会有100个线程全部被执行。处理源码如下:
    Java中Queue的一些常用方法:
    poll() 移除并返问队列头部的元素
    在这里插入图片描述
    解释:该策略将丢弃最老的一个请求,也就是即将被执行的一个任务,并尝试再次提交当前任务。(问:为什么是弹出队列的头元素呢?答:是因为队列的先进先出原则,所以队列的头元素肯定是最老的一个请求,而DiscardOldestPolicy策略就是把最老的请求废除,把这个机会(相当于抢了最老的请求的吃到嘴边的肉)留给当前新提交到线程池里面的线程(这个时候线程池已经满了,队列也已经满了,如果不满的话,也不会有拒绝策略了!))

    策略4:ThreadPoolExecutor.DiscardPolicy
    用于被拒绝任务的处理程序,默认情况下它将丢弃被拒绝的任务。
    运行结果也不会全部执行100个线程。
    源码如下,实际就是对线程不执行操作:
    在这里插入图片描述
    在这里插入图片描述

     

    1. public class ExecutorsDemo {

    2.     private static ExecutorService executor = Executors.newFixedThreadPool(15);

    3.     public static void main(String[] args) {

    4.         for (int i = 0; i < Integer.MAX_VALUE; i++) {

    5.             executor.execute(new SubThread());

    6.         }

    7.     }

    8. }

    9.  
    10. class SubThread implements Runnable {

    11.     @Override

    12.     public void run() {

    13.         try {

    14.             Thread.sleep(10000);

    15.         } catch (InterruptedException e) {

    16.             //do nothing

    17.         }

    18.     }

    19. }



       

      从图中可以看出,当提交一个新任务到线程池时,线程池的处理流程如下。

      1)线程池判断核心线程池里的线程是否都在执行任务。如果不是,则创建一个新的工作 线程来执行任务。如果核心线程池里的线程都在执行任务,则进入下个流程。

      2)线程池判断工作队列是否已经满。如果工作队列没有满,则将新提交的任务存储在这 个工作队列里。如果工作队列满了,则进入下个流程。

      3)线程池判断线程池的线程是否都处于工作状态。如果没有,则创建一个新的工作线程 来执行任务。如果已经满了,则交给饱和策略来处理这个任务。

      ·

      ·keepAliveTime(线程活动保持时间):线程池的工作线程空闲后,保持存活的时间。所以, 如果任务很多,并且每个任务执行的时间比较短,可以调大时间,提高线程的利用率。

       

      SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用 移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于Linked-BlockingQueue,静态工 厂方法Executors.newCachedThreadPool使用了这个队列。

      10.2 ThreadPoolExecutor详解 Executor框架最核心的类是ThreadPoolExecutor,它是线程池的实现类,主要由下列4个组

      件构成。

      ·corePool:核心线程池的大小。

      ·maximumPool:最大线程池的大小。

      ·BlockingQueue:用来暂时保存任务的工作队列。

      ·RejectedExecutionHandler:当ThreadPoolExecutor已经关闭或ThreadPoolExecutor已经饱和 时(达到了最大线程池大小且工作队列已满),execute()方法将要调用的Handler。

      ·通过Executor框架的工具类Executors,可以创建3种类型的ThreadPoolExecutor。 ·FixedThreadPool。
      ·SingleThreadExecutor。
      ·CachedThreadPool。

      下面将分别介绍这3种ThreadPoolExecutor。

      10.2.1 FixedThreadPool详解 FixedThreadPool被称为可重用固定线程数的线程池。下面是FixedThreadPool的源代码实

      现。

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

      FixedThreadPool的corePoolSize和maximumPoolSize都被设置为创建FixedThreadPool时指 定的参数nThreads。

      当线程池中的线程数大于corePoolSize时,keepAliveTime为多余的空闲线程等待新任务的 最长时间,超过这个时间后多余的线程将被终止。这里把keepAliveTime设置为0L,意味着多余 的空闲线程会被立即终止。

      FixedThreadPool的execute()方法的运行示意图如图10-4所示。

      图10-4 FixedThreadPool的execute()的运行示意图 对图10-4的说明如下。

      1)如果当前运行的线程数少于corePoolSize,则创建新线程来执行任务。 2)在线程池完成预热之后(当前运行的线程数等于corePoolSize),将任务加入

      LinkedBlockingQueue。

      3)线程执行完1中的任务后,会在循环中反复从LinkedBlockingQueue获取任务来执行。

      FixedThreadPool使用无界队列LinkedBlockingQueue作为线程池的工作队列(队列的容量为 Integer.MAX_VALUE)。使用无界队列作为工作队列会对线程池带来如下影响。

      1)当线程池中的线程数达到corePoolSize后,新任务将在无界队列中等待,因此线程池中 的线程数不会超过corePoolSize。

      2)由于1,使用无界队列时maximumPoolSize将是一个无效参数。

      3)由于1和2,使用无界队列时keepAliveTime将是一个无效参数。

      4)由于使用无界队列,运行中的FixedThreadPool(未执行方法shutdown()或 shutdownNow())不会拒绝任务(不会调用RejectedExecutionHandler.rejectedExecution方法)。

      10.2.2 SingleThreadExecutor详解 SingleThreadExecutor是使用单个worker线程的Executor。下面是SingleThreadExecutor的源

      代码实现。

       

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

      SingleThreadExecutor的corePoolSize和maximumPoolSize被设置为1。其他参数与 FixedThreadPool相同。SingleThreadExecutor使用无界队列LinkedBlockingQueue作为线程池的工 作队列(队列的容量为Integer.MAX_VALUE)。SingleThreadExecutor使用无界队列作为工作队列 对线程池带来的影响与FixedThreadPool相同,这里就不赘述了。

      SingleThreadExecutor的运行示意图如图10-5所示。

      图10-5 SingleThreadExecutor的execute()的运行示意图 对图10-5的说明如下。

      1)如果当前运行的线程数少于corePoolSize(即线程池中无运行的线程),则创建一个新线 程来执行任务。

      2)在线程池完成预热之后(当前线程池中有一个运行的线程),将任务加入Linked- BlockingQueue。

      3)线程执行完1中的任务后,会在一个无限循环中反复从LinkedBlockingQueue获取任务来 执行。

      10.2.3 CachedThreadPool详解 CachedThreadPool是一个会根据需要创建新线程的线程池。下面是创建CachedThread-

      Pool的源代码。

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

      }

      CachedThreadPool的corePoolSize被设置为0,即corePool为空;maximumPoolSize被设置为 Integer.MAX_VALUE,即maximumPool是无界的。这里把keepAliveTime设置为60L,意味着 CachedThreadPool中的空闲线程等待新任务的最长时间为60秒,空闲线程超过60秒后将会被 终止。

      FixedThreadPool和SingleThreadExecutor使用无界队列LinkedBlockingQueue作为线程池的 工作队列。CachedThreadPool使用没有容量的SynchronousQueue作为线程池的工作队列,但 CachedThreadPool的maximumPool是无界的。这意味着,如果主线程提交任务的速度高于 maximumPool中线程处理任务的速度时,CachedThreadPool会不断创建新线程。极端情况下, CachedThreadPool会因为创建过多线程而耗尽CPU和内存资源。

      CachedThreadPool的execute()方法的执行示意图如图10-6所示。

      图10-6 CachedThreadPool的execute()的运行示意图 对图10-6的说明如下。

      1)首先执行SynchronousQueue.offer(Runnable task)。如果当前maximumPool中有空闲线程 正在执行SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS),那么主线程执行 offer操作与空闲线程执行的poll操作配对成功,主线程把任务交给空闲线程执行,execute()方 法执行完成;否则执行下面的步骤2)。

      2)当初始maximumPool为空,或者maximumPool中当前没有空闲线程时,将没有线程执行 SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS)。这种情况下,步骤1)将失 败。此时CachedThreadPool会创建一个新线程执行任务,execute()方法执行完成。

      3)在步骤2)中新创建的线程将任务执行完后,会执行 SynchronousQueue.poll(keepAliveTime,TimeUnit.NANOSECONDS)。这个poll操作会让空闲线

      程最多在SynchronousQueue中等待60秒钟。如果60秒钟内主线程提交了一个新任务(主线程执 行步骤1)),那么这个空闲线程将执行主线程提交的新任务;否则,这个空闲线程将终止。由于 空闲60秒的空闲线程会被终止,因此长时间保持空闲的CachedThreadPool不会使用任何资源。

      前面提到过,SynchronousQueue是一个没有容量的阻塞队列。每个插入操作必须等待另一 个线程的对应移除操作,反之亦然。CachedThreadPool使用SynchronousQueue,把主线程提交的 任务传递给空闲线程执行。CachedThreadPool中任务传递的示意图如图10-7所示。

      图10-7 CachedThreadPool的任务传递示意图

      10.3 ScheduledThreadPoolExecutor详解

      ScheduledThreadPoolExecutor继承自ThreadPoolExecutor。它主要用来在给定的延迟之后运 行任务,或者定期执行任务。ScheduledThreadPoolExecutor的功能与Timer类似,但 ScheduledThreadPoolExecutor功能更强大、更灵活。Timer对应的是单个后台线程,而 ScheduledThreadPoolExecutor可以在构造函数中指定多个对应的后台线程数。

     
    展开全文
  • 线程池Executors详解

    2021-03-22 11:06:35
    /** * 线程池 */ public class Test1 extends Object{ public static void main(String[] args) { //创建一个可重用,固定线程数的线程池 ExecutorService threadPool = Executors.newFixedThreadPool(2);...

    为什么要用线程池呢?

    一是减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务;

    二是可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

    线程池的基本思想是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。

    可见,线程池的作用主要是限制系统中执行线程的数量。   根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果。这是因为,线程少了会浪费系统资源,线程多了会造成系统拥挤、效率不高。用线程池控制线程数量,其他线程排队等候。一个任务执行完毕,再从队列中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,如果线程池中有等待的工作线程,就可以开始运行了;否则进入等待队列。

    Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。ThreadPoolExecutor是Executors类的底层实现。我们先介绍下Executors。

    在使用线程池之前,必须知道如何去创建一个线程池。

    1.固定大小的线程池

    package com.itszt.test3;

    import java.util.concurrent.ExecutorService;

    import java.util.concurrent.Executors;

    /**

    * 线程池

    */

    public class Test1 extends Object{

    public static void main(String[] args) {

    //创建一个可重用,固定线程数的线程池

    ExecutorService threadPool = Executors.newFixedThreadPool(2);

    //创建实现了Runnable接口的类,如Thread

    MyThread t1 = new MyThread();

    MyThread t2 = new MyThread();

    MyThread t3 = new MyThread();

    MyThread t4 = new MyThread();

    //将线程放入池中执行

    threadPool.execute(t1);

    threadPool.execute(t2);

    threadPool.execute(t3);

    threadPool.execute(t4);

    //关闭线程池

    threadPool.shutdown();

    }

    }

    class MyThread extends Thread{

    @Override

    public void run() {

    System.out.println(Thread.currentThread().getName()+"正在执行...");

    }

    }

    执行结果如下:

    pool-1-thread-1正在执行...

    pool-1-thread-2正在执行...

    pool-1-thread-1正在执行...

    pool-1-thread-1正在执行...

    2.单任务线程池

    复用上述代码,将上例中创建线程池的代码改为:

    ExecutorService threadPool = Executors.newSingleThreadExecutor();

    执行结果如下:

    pool-1-thread-1正在执行...

    pool-1-thread-1正在执行...

    pool-1-thread-1正在执行...

    pool-1-thread-1正在执行...

    3.可变尺寸的线程池

    改变创建线程池的方法:

    ExecutorService threadPool = Executors.newCachedThreadPool();

    执行结果如下:

    pool-1-thread-2正在执行...

    pool-1-thread-1正在执行...

    pool-1-thread-3正在执行...

    pool-1-thread-4正在执行...

    4.延迟连接池

    修改创建线程池的方式:

    ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(2);

    MyThread t1 = new MyThread();

    MyThread t2 = new MyThread();

    MyThread t3 = new MyThread();

    MyThread t4 = new MyThread();

    //延迟执行

    threadPool.schedule(t1,5, TimeUnit.MILLISECONDS);

    threadPool.schedule(t2,5, TimeUnit.MILLISECONDS);

    threadPool.schedule(t3,5, TimeUnit.MILLISECONDS);

    threadPool.schedule(t4,5, TimeUnit.MILLISECONDS);

    //关闭线程池

    threadPool.shutdown();

    5.单任务延迟连接池

    修改创建线程池的方式:

    ScheduledExecutorService threadPool = Executors.newSingleThreadScheduledExecutor();

    6.自定义线程池

    package com.itszt.test3;

    import java.text.SimpleDateFormat;

    import java.util.Date;

    import java.util.concurrent.ArrayBlockingQueue;

    import java.util.concurrent.BlockingQueue;

    import java.util.concurrent.ThreadPoolExecutor;

    import java.util.concurrent.TimeUnit;

    /**

    * 自定义线程池

    */

    public class Test2 {

    public static void main(String[] args) {

    //创建等待队列

    BlockingQueue bQueue = new ArrayBlockingQueue(20);

    //创建一个单线程执行程序,可安排在给定延迟时间后执行

    ThreadPoolExecutor pool = new ThreadPoolExecutor(2,3,2,TimeUnit.MILLISECONDS,bQueue);

    //创建实现了Runnable接口的类,如Thread

    MyThread t1 = new MyThread();

    MyThread t2 = new MyThread();

    MyThread t3 = new MyThread();

    MyThread t4 = new MyThread();

    //将线程放入池中执行

    pool.execute(t1);

    pool.execute(t2);

    pool.execute(t3);

    pool.execute(t4);

    //关闭线程池

    pool.shutdown();

    }

    }

    class MyThread extends Thread{

    @Override

    public void run() {

    System.out.println(Thread.currentThread().getName()+"正在执行..."+System.currentTimeMillis());

    }

    }

    展开全文
  • public static ExecutorService getInstance() { if (executorService == null) { executorService = Executors.newFixedThreadPool(3); } return executorService; } } package com.glch.threads; import java....

            newFixedThreadPool和newCachedThreadPool是创建线程池的两种方式,其中newFixedThreadPool是创建固定数量为 threads的线程数,其阻塞队列用的是LinkedBlockingQueue,队列大小容量为Integer.MAX_VALUE;newCachedThreadPool是创建一个可缓存的线程池,不会对线程池的大小做任何的限制,其大小是依赖于操作系统能够创建的最大线程大小。

    缺点:

    newFixedThreadPool:阻塞队列容量大,当队列中推积的数据过多会造成CPU内存过高异常,导致程序异常退出。

    newCachedThreadPool:线程池大小没有做限制,当任务来了直接创建线程,易导致资源消耗殆尽。

    newCachedThreadPool适合执行时间比较小的任务,因为newCachedThreadPool线程池的大小超过了要处理任务的数量,空闲线程超过60s,那么空闲线程就会被回收,即能够复用空闲线程,减少创建对象和回收对象带来的开销。不同于newFixedThreadPool,newFixedThreadPool是创建固定容量大小的线程池,也就是即使空闲时间超过一定时间,也不会回收空闲线程,始终占用着cpu资源,但是其优点在于能够控制住最大并发数。

    package com.glch.threads;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * @author zzl
     * @Date 2021/9/15
     * @description 创建一个固定数量的线程池
     */
    public class FixThreadPool {
        private static ExecutorService executorService = null;
    
        public static ExecutorService getInstance() {
            if (executorService == null) {
                executorService = Executors.newFixedThreadPool(3);
            }
            return executorService;
        }
    }
    
    
    
    package com.glch.threads;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    /**
     * @author zzl
     * @Date 2021/9/15
     * @description 创建一个可以缓存的线程池
     */
    public class CacheThreadPool {
        private static ExecutorService executorService;
    
        public static ExecutorService getInstance(){
            if(executorService==null){
                executorService = Executors.newCachedThreadPool();
            }
            return executorService;
        }
    }
    
    
    package com.glch.threads;
    
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Future;
    
    /**
     * @author zzl
     * @Date 2021/9/15
     * @description
     */
    public class FixThreadPoolTest {
        public static void main(String[] args) {
            // 测试newFixThreadPool
            ExecutorService pool = FixThreadPool.getInstance();
            // execute的方式,这种方式无法从线程池中返回结果集,即没有返回值
            for (int i = 0; i < 4; i++) {
                pool.execute(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("666");
                        try {
                            Thread.sleep(6000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
            for (int i = 0; i < 4; i++) {
                pool.execute(new Thread(() -> {
                    System.out.println("666");
                    try {
                        Thread.sleep(6000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }));
            }
            // summit的方式,有返回值,且方便做异常处理,Future.get()可捕获异常
            for (int i = 0; i < 4; i++) {
                Future future = pool.submit(new Thread(() -> {
                    System.out.println("666");
                    try {
                        Thread.sleep(6000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }));
            }
    
        }
    }
    
    

    展开全文
  • 前记 首先两者本质都是通过调用ThreadPoolExecutor生成一个ExecutorService 具体源码: Executors生成线程池的四种静态方法 newFixedThreadPool public static ExecutorService newFixedThreadPool(int nThreads) { ...
  • java通过Executors可以创建五种方式的对线程: 第一种: 创建一个单线程。 第二种: 创建一个可变的多线程。: 第三种: 创建一个固定线程数的多线程。 第四种:创建一个定时的多线程。 第五种:创建可以调参数的多...
  • java中Executors

    2021-04-17 08:10:59
    返回 Callable 对象,调用它时可运行给定特权的异常操作并返回其结果。参数: action - 要运行的特权异常操作返回: 一个 callable 对象抛出: NullPointerException - 如果 action 为 null19、public static ...
  • 1. 通过Executors创建线程池的弊端在创建线程池的时候,大部分人还是会选择使用Executors去创建。下面是创建定长线程池(FixedThreadPool)的一个例子,严格来说,...
  • executors 执行器 job 任务 jobstores 任务存储 triggers 触发器 schedulers 调度程序 这一篇主要瞅瞅 events 事件 和 executors 执行器 events 事件 event 主要是 APScheduler 中触发的事件类,我们可以通过 add_...
  • 下面列举一些你可以通过 Executors 类来创建的线程池的类型: Single Thread Executor : 只有一个线程的线程池,因此所有提交的任务是顺序执行,代码:Executors.newSingleThreadExecutor() Cached Thread Pool : ...
  • 慎用Executors类中newFixedThreadPool()和newCachedThreadPool()
  • ExecutorService 于 Executors 在java中的区别有哪些发布时间:2020-11-23 16:24:50来源:亿速云阅读:91作者:Leah今天就跟大家聊聊有关ExecutorService 于 Executors 在java中的区别有哪些,可能很多人都不太了解...
  • 1. 线程池简介 线程池是指预先创建好一定数量的线程对象,存入缓冲池中,需要用的时候直接从缓冲池中取出,线程结束之后会再回到...(2) 使用Executors 工具类 Executor接口的3种实现 ① newSingleThreadExecutor(); /
  • java并发(1)-Executors关于java创建多线程常用的两种方式,我就不多说了,无非就是实现Runnable和继承Thread。那么我们现在来说说其他的方法。Executors是什么Executors,我们称之为执行器,用于管理Thread对象,...
  • 谈谈为什么禁止使用Executors创建线程池?什么是ExecutorsExecutors存在什么问题?Executors为什么存在缺陷?如何正确创建线程池? 首先,可以通过Executors静态工厂构建线程池,但是一般不建议这样使用。 什么是...
  • Executors

    2021-06-06 01:28:43
    Executors.newSingleThreadExecutor() new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue())); 是一个单线程的线程池,只有一个核心线程,都通过这个来执行 线程池中最多执行1个线程,...
  • 为什么线程池不允许使用Executors去创建?ExecutorsExecutors存在什么问题Executors为什么会OOM创建线程池的正确姿势 Executors Executors 是一个Java中的工具类。提供工厂方法来创建不同类型的线程池。 Executors...
  • JAVA并发 Executors框架

    2021-03-08 18:12:27
    Executors框架介绍Executors框架其内部采用了线程池机制,他在java.util.cocurrent包下,通过该框架来控制线程的启动、执行、关闭,可以简化并发编程的操作。因此,通过Executors来启动线程比使用Thread的start方法...
  • 文章目录Executors简介newCachedThreadPoolnewFixedThreadPoolnewScheduledThreadPoolnewSingleThreadExecutor线程池的四种拒绝策略 Executors简介 Executors类在java.util.concurrent包下。 Java通过Executors提供...
  • Executors 源码解析(JDK8)

    千次阅读 2021-11-08 21:48:52
    * * * @since 1.5 * @author Doug Lea */ public class Executors { 当前包内定义的 Executor 、 ExecutorService 、 ScheduledExecutorService 、 ThreadFactory 和 Callable 类的工厂和实用程序方法。 此类支持...
  • Executors创建线程的其他方式: newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 newFixedThreadPool 创建一个定长线程池,可控制线程最大...
  • 因此,群集中核心的可用总数= 15 x 10 = 150 –num-executors =(群集中核心的可用总数/每个executors分配的核心数)= 150/5 = 30 为ApplicationManager留下预留1个executors的资源, 即–num-executors = 29 每个...
  • Executors 1)newFixedThreadPool public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue())...
  • 成员继承体系如下图: 以上成员均在java.util.concurrent包中,此外还有Executors工具类,Executors类扮演着线程池工厂的角色,通过Executors可以获取一个特定功能的线程池,下面是Executors工厂类的主要方法: //...
  • 1. CachedThreadPool线程池 2. FixedThreadPool线程池 3. SingleThreadExecutor线程池
  • 来源 |http://cnblogs.com/zjfjava/p/11227456.html1. 通过Executors创建线程池的弊端在创建线程池的时候,大部分人还是会选择使用Exec...
  • private static ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor(); public static void main(String[] args) { System.out.println("开始:"+ LocalTime.now()); service....
  • 比如: Executors.newSingleThreadExecutor() 创建一个只有一个线程的线程池, Executors.newFixedThreadPool(int numOfThreads)来创建固定线程数的线程池, Executors.newCachedThreadPool()创建一个可缓存线程池,...
  • Executors.newFixedThreadPool public class ThreadPoolDemo { public static void main(String[] args) { ExecutorService threadPool = Executors.newFixedThreadPool(5);//一池5个处理线程 try { for (int...
  • import java.text.SimpleDateFormat; import java.util.Date;...import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; /** * https://blog.csdn.net/w.

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 155,437
精华内容 62,174
关键字:

executors

友情链接: mfcc.zip