抖音资讯

douyinzx

线程池的创建方式有几种(七种常见的线程池)

iseeyu2年前 (2024-05-04)抖音资讯112

在 Java 语言中,并发编程都是通过创建线程池来实现的,而线程池的创建方式也有很多种,每种线程池的创建方式都对应了不同的使用场景,总体来说线程池的创建可以分为以下两类:

  • 通过 ThreadPoolExecutor 手动创建线程池。
  • 通过 Executors 执行器自动创建线程池。

 

而以上两类创建线程池的方式,又有 7 种具体实现方法,这 7 种实现方法分别是:

  1. Executors.newFixedThreadPool:创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待。
  2. Executors.newCachedThreadPool:创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程。
  3. Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执行顺序。
  4. Executors.newScheduledThreadPool:创建一个可以执行延迟任务的线程池。
  5. Executors.newSingleThreadScheduledExecutor:创建一个单线程的可以执行延迟任务的线程池。
  6. Executors.newWorkStealingPool:创建一个抢占式执行的线程池(任务执行顺序不确定)【JDK 1.8 添加】。
  7. ThreadPoolExecutor:手动创建线程池的方式,它创建时最多可以设置 7 个参数。

接下来我们分别来看这 7 种线程池的具体使用。

1.FixedThreadPool

创建一个固定大小的线程池,可控制并发线程数。

使用 FixedThreadPool 创建 2 个固定大小的线程池,具体实现代码如下:

public static void fixedThreadPool() {
    // 创建 2 个线程的线程池
    ExecutorService threadPool = Executors.newFixedThreadPool(2);

    // 创建任务
    Runnable runnable = new Runnable() {
        @Override public void run() {
            System.out.println("任务被执行,线程:" + Thread.currentThread().getName());
        }
    };

    // 线程池执行任务(一次添加 4 个任务)
    // 执行任务的方法有两种:submit 和 execute
    threadPool.submit(runnable);  // 执行方式 1:submit
    threadPool.execute(runnable); // 执行方式 2:execute
    threadPool.execute(runnable);
    threadPool.execute(runnable);
}

以上程序的执行结果如下图所示:

 

如果觉得以上方法比较繁琐,还用使用以下简单的方式来实现线程池的创建和使用:

public static void fixedThreadPool() {
    // 创建线程池
    ExecutorService threadPool = Executors.newFixedThreadPool(2);
    // 执行任务
    threadPool.execute(() -> {
        System.out.println("任务被执行,线程:" + Thread.currentThread().getName());
    });
}

2.CachedThreadPool

创建一个可缓存的线程池,若线程数超过任务所需,那么多余的线程会被缓存一段时间后才被回收,若线程数不够,则会新建线程。

CachedThreadPool 使用示例如下:

public static void cachedThreadPool() {
    // 创建线程池
    ExecutorService threadPool = Executors.newCachedThreadPool();
    // 执行任务
    for (int i = 0; i < 10; i++) {
        threadPool.execute(() -> {
            System.out.println("任务被执行,线程:" + Thread.currentThread().getName());
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
            }
        });
    }
}

以上程序的执行结果如下图所示:

 

从上述结果可以看出,线程池创建了 10 个线程来执行相应的任务。

使用场景

CachedThreadPool 是根据短时间的任务量来决定创建的线程数量的,所以它适合短时间内有突发大量任务的处理场景。

3.SingleThreadExecutor

创建单个线程的线程池,它可以保证先进先出的执行顺序。

SingleThreadExecutor 使用示例如下:

public static void singleThreadExecutor() {
    // 创建线程池
    ExecutorService threadPool = Executors.newSingleThreadExecutor();
    // 执行任务
    for (int i = 0; i < 10; i++) {
        final int index = i;
        threadPool.execute(() -> {
            System.out.println(index + ":任务被执行");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
            }
        });
    }
}

以上程序的执行结果如下图所示:

 

单个线程的线程池有什么意义?

单个线程的线程池相比于线程来说,它的优点有以下 2 个:

  • 可以复用线程:即使是单个线程池,也可以复用线程。
  • 提供了任务管理功能:单个线程池也拥有任务队列,在任务队列可以存储多个任务,这是线程无法实现的,并且当任务队列满了之后,可以执行拒绝策略,这些都是线程不具备的。

4.ScheduledThreadPool

创建一个可以执行延迟任务的线程池。

使用示例如下:

public static void scheduledThreadPool() {
    // 创建线程池
    ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);
    // 添加定时执行任务(1s 后执行)
    System.out.println("添加任务,时间:" + new Date());
    threadPool.schedule(() -> {
        System.out.println("任务被执行,时间:" + new Date());
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
        }
    }, 1, TimeUnit.SECONDS);
}

以上程序的执行结果如下图所示:

 

从上述结果可以看出,任务在 1 秒之后被执行了,实现了延迟 1s 再执行任务。

5.SingleThreadScheduledExecutor

创建一个单线程的可以执行延迟任务的线程池,此线程池可以看作是 ScheduledThreadPool 的单线程池版本。

它的使用示例如下:

public static void SingleThreadScheduledExecutor() {
    // 创建线程池
    ScheduledExecutorService threadPool = Executors.newSingleThreadScheduledExecutor();
    // 添加定时执行任务(2s 后执行)
    System.out.println("添加任务,时间:" + new Date());
    threadPool.schedule(() -> {
        System.out.println("任务被执行,时间:" + new Date());
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
        }
    }, 2, TimeUnit.SECONDS);
}

以上程序的执行结果如下图所示:

 

从上述结果可以看出,任务在 2 秒之后被执行了。

6.newWorkStealingPool

创建一个抢占式执行的线程池(任务执行顺序不确定),此方法是 JDK 1.8 版本新增的,因此只有在 JDK 1.8 以上的程序中才能使用。

newWorkStealingPool 使用示例如下:

public static void workStealingPool() {
    // 创建线程池
    ExecutorService threadPool = Executors.newWorkStealingPool();
    // 执行任务
    for (int i = 0; i < 10; i++) {
        final int index = i;
        threadPool.execute(() -> {
            System.out.println(index + " 被执行,线程名:" + Thread.currentThread().getName());
        });
    }
    // 确保任务执行完成
    while (!threadPool.isTerminated()) {
    }
}

以上程序的执行结果如下图所示:

 

从上述结果可以看出,任务的执行顺序是不确定的,因为它是抢占式执行的。

7.ThreadPoolExecutor

ThreadPoolExecutor 是最原始、也是最推荐的手动创建线程池的方式,它在创建时最多提供 7 个参数可供设置。

ThreadPoolExecutor 使用示例如下:

public static void myThreadPoolExecutor() {
    // 创建线程池
    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));
    // 执行任务
    for (int i = 0; i < 10; i++) {
        final int index = i;
        threadPool.execute(() -> {
            System.out.println(index + " 被执行,线程名:" + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
    }
}

以上程序的执行结果如下图所示:

 

ThreadPoolExecutor 相比于其他创建线程池的优势在于,它可以通过参数来控制最大任务数和拒绝策略,让线程池的执行更加透明和可控,所以在阿里巴巴《Java开发手册》是这样规定的:

【强制要求】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

 

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

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

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

总结

线程池的创建方式总共有以下 7 种:

  1. Executors.newFixedThreadPool:创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待。
  2. Executors.newCachedThreadPool:创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程。
  3. Executors.newSingleThreadExecutor:创建单个线程数的线程池,它可以保证先进先出的执行顺序。
  4. Executors.newScheduledThreadPool:创建一个可以执行延迟任务的线程池。
  5. Executors.newSingleThreadScheduledExecutor:创建一个单线程的可以执行延迟任务的线程池。
  6. Executors.newWorkStealingPool:创建一个抢占式执行的线程池(任务执行顺序不确定)【JDK 1.8 添加】。
  7. ThreadPoolExecutor:手动创建线程池的方式,它创建时最多可以设置 7 个参数。

线程池的创建推荐使用最后一种 ThreadPoolExecutor 的方式来创建,因为使用它可以明确线程池的运行规则,规避资源耗尽的风险

扫描二维码推送至手机访问。

版权声明:本文由西安泽虎代运营发布,如需转载请注明出处。

转载请注明出处https://www.0291.com.cn/post/45590.html

相关文章

如何把别人抖音变成自己作品又不违规(视频搬运做二次剪辑的教学)

如何把别人抖音变成自己作品又不违规(视频搬运做二次剪辑的教学)

怎么把别人的视频变成自己的作品还不会侵权呢?分享3个小技巧给大家,记得点赞评论加收藏噢!别告诉我没有提醒你啊!我们接着往下看: 有很多家人朋友会很奇怪,为什么我的作品下面还带着别人的视频,难道不怕别人说我侵权吗?很负责任的告诉你,但凡视频做得好的人都不会那样子去说你!他还会感谢你,感...

今日头条的发文要求是什么?今日头条发文规定

今日头条的发文要求是什么?今日头条发文规定

今日头条的发文要求是什么?今日头条发文规定 1.不得发布违法内容 用户在写今日头条时要注意避免危害社会公德.扰乱社会秩序.破坏社会稳定的内容,否则文章的内容将无法通过审查。一般来说,非法内容的发布会导致用户账户被禁止扣分,或永久标题。 2.文章内容不得抄袭侵权侵...

大健康行业素材风向标-6月 | 快手营销平台

大健康行业素材风向标-6月 | 快手营销平台

大健康商家直投链路sop 一、转化链路概览  大健康行业长期投放信息流场景,线上获取线索+线下销售跟进,通过电销、粉丝营销形式完成用户转化,其存在转化周期长、深度数据难回传、运营成本高等难题,在电商场景探索中,实现通过快手信息流广告引流至第三方店铺,用户实现在线支付,提升转化效率、降低...

抖音秒关注秒取消对方能看到吗(刚关注就取关的设置)

抖音秒关注秒取消对方能看到吗(刚关注就取关的设置)

说到抖音这款短视频APP相信大家都是清楚的,很多人的手机里面都有下载抖音软件,我们在业余的时间可以通过刷抖音的方式打发时间,然后上面有个点赞的功能,如果点赞又取消的话对方是否会知道呢?     抖音点赞又取消后,对方是知道的,因为点赞的时候,会有...

微信视频号启动期的如何运营?视频号新手入门教程

欢迎回到 @视频号之家 ,每天为您分享视频号运营的(、、涨粉、)。只讲干货,不说费话。本文4000字,预计阅读5min,手把手教你,视频号新手入门教程,彻底让你学会视频号启动初期的和运营。本文你将得到:一、理解重视初期传播的3条理由;二、掌握3种视频号冷启动的方法;全面都是...

B站财报里的冰与火,营收变慢亏损扩大

雷达财经出品 文|李亦辉 编|深海提出盈利目标后,B站的亏损反而扩大了。6月9日,B站发布2022年第一季度财报。财报显示,B站一季度营收达50.54亿元,同比增长30%;净亏损22.82亿元,同比扩大超150%;调整后净亏损16.5亿元,同比扩大超85%。从营收增速看,B...

现在,非常期待与您的又一次邂逅

我们努力让每一部企业宣传片和抖音短视频成为商业大片