前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >线程池创建方式

线程池创建方式

原创
作者头像
ruochen
修改2021-11-24 13:54:25
6930
修改2021-11-24 13:54:25
举报
文章被收录于专栏:若尘的技术专栏

1.FixedThreadPool

创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待。

源码

90分钟10个手写案例,从源码底层给你讲解7种线程池创建方式

使用示例如下:

代码语言:txt
复制
package com.test.thread;
代码语言:txt
复制
import java.util.concurrent.ExecutorService;
代码语言:txt
复制
import java.util.concurrent.Executors;
代码语言:txt
复制
/**
代码语言:txt
复制
 * @author :biws
 * @date :Created in 2020/12/17 19:23
 * @description:测试FixedThreadPool
 */
public class testThread1 {
 public static void fixedThreadPool() {
 // 创建 2 个数据级的线程池
 ExecutorService threadPool = Executors.newFixedThreadPool(2);
代码语言:txt
复制
 // 创建任务
代码语言:txt
复制
 Runnable runnable = new Runnable() {
代码语言:txt
复制
 @Override
代码语言:txt
复制
 public void run() {
代码语言:txt
复制
 System.out.println("任务被执行,线程:" + Thread.currentThread().getName());
代码语言:txt
复制
 }
代码语言:txt
复制
 };
代码语言:txt
复制
 // 线程池执行任务(一次添加 4 个任务)
代码语言:txt
复制
 // 执行任务的方法有两种:submit 和 execute
代码语言:txt
复制
 threadPool.submit(runnable);  // 执行方式 1:submit
代码语言:txt
复制
 threadPool.execute(runnable); // 执行方式 2:execute
代码语言:txt
复制
 threadPool.execute(runnable);
代码语言:txt
复制
 threadPool.execute(runnable);
代码语言:txt
复制
 }
代码语言:txt
复制
 public static void main(String[] args) {
代码语言:txt
复制
 fixedThreadPool();
代码语言:txt
复制
 }
代码语言:txt
复制
}

执行结果如下:

90分钟10个手写案例,从源码底层给你讲解7种线程池创建方式

后来我将测试数量提交到100,而线程池中处理线程得数量增加到4

90分钟10个手写案例,从源码底层给你讲解7种线程池创建方式

执行结果

90分钟10个手写案例,从源码底层给你讲解7种线程池创建方式

最开始,提交4个线程执行,之后的线程会在队列中排序等待被执行

如果觉得以上方法比较繁琐,还可以用更简单的使用方法,如下代码所示:

代码语言:txt
复制
package com.test.thread;
代码语言:txt
复制
import java.util.concurrent.ExecutorService;
代码语言:txt
复制
import java.util.concurrent.Executors;
代码语言:txt
复制
/**
代码语言:txt
复制
 * @author :biws
 * @date :Created in 2020/12/17 19:27
 * @description:FixedThreadPool进阶写法
 */
public class testThreadG {
 public static void fixedThreadPool() {
 // 创建线程池
 ExecutorService threadPool = Executors.newFixedThreadPool(2);
 /*
 * 执行任务
 * 为了个之前得进行统一,所以这里通过for循环,同样是提交4个执行*/
 for(int i=1;i<=4;i++) {
 threadPool.execute(() -> {
 System.out.println("任务被执行,线程:" + Thread.currentThread().getName());
 });
 }
 }
代码语言:txt
复制
 public static void main(String[] args) {
代码语言:txt
复制
 fixedThreadPool();
代码语言:txt
复制
 }
代码语言:txt
复制
}

2.CachedThreadPool

创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间后会回收,若线程数不够,则新建线程。

源码

90分钟10个手写案例,从源码底层给你讲解7种线程池创建方式

参数详解:

corePoolSize = 0,

maximumPoolSize设置为Integer.MAX_VALUE,代表没有核心线程,非核心线程是无界的;keepAliveTime = 60L,空闲线程等待新任务的最长时间是60s;

用了阻塞队列SynchronousQueue,是一个不存储元素的阻塞队列

使用示例如下:

代码语言:txt
复制
package com.test.thread;
代码语言:txt
复制
import java.util.concurrent.ExecutorService;
代码语言:txt
复制
import java.util.concurrent.Executors;
代码语言:txt
复制
import java.util.concurrent.TimeUnit;
代码语言:txt
复制
/**
代码语言:txt
复制
 * @author :biws
 * @date :Created in 2020/12/17 19:38
 * @description:测试CachedThreadPool
 */
public class testThread2 {
 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) {
 }
 });
 }
 }
代码语言:txt
复制
 public static void main(String[] args) {
代码语言:txt
复制
 cachedThreadPool();
代码语言:txt
复制
 }
代码语言:txt
复制
}
代码语言:txt
复制
}

执行结果如下:

90分钟10个手写案例,从源码底层给你讲解7种线程池创建方式

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

而如果我将sleep注释之后,再来看执行结果

90分钟10个手写案例,从源码底层给你讲解7种线程池创建方式

我想这个结果一目了然吧,大家如果可以的话,可以把这个执行数据提升一下,然后再查看一下结果应该会更明显

3.SingleThreadExecutor

创建单个线程数的线程池,它可以保证先进先出的执行顺序。为了能让大家看的更清楚,所以每个线程在执行得时候我都添加了他的时间

使用示例如下:

代码语言:txt
复制
package com.test.thread;
代码语言:txt
复制
import java.util.concurrent.ExecutorService;
代码语言:txt
复制
import java.util.concurrent.Executors;
代码语言:txt
复制
import java.util.concurrent.TimeUnit;
代码语言:txt
复制
/**
代码语言:txt
复制
 * @author :biws
 * @date :Created in 2020/12/17 20:17
 * @description:测试singleThreadExecutor
 */
public class testThread4 {
代码语言:txt
复制
 public static void singleThreadExecutor() {
代码语言:txt
复制
 // 创建线程池
代码语言:txt
复制
 ExecutorService threadPool = Executors.newSingleThreadExecutor();
代码语言:txt
复制
 // 执行任务
代码语言:txt
复制
 for (int i = 0; i < 10; i++) {
代码语言:txt
复制
 final int index = i;
代码语言:txt
复制
 threadPool.execute(() -> {
代码语言:txt
复制
 new Thread();
代码语言:txt
复制
 System.out.println(Thread.currentThread().getName()+"开始时间[" + new java.util.Date().getTime());
代码语言:txt
复制
 System.out.println(index + ":任务被执行");
代码语言:txt
复制
 try {
代码语言:txt
复制
 TimeUnit.SECONDS.sleep(1);
代码语言:txt
复制
 } catch (InterruptedException e) {
代码语言:txt
复制
 }
代码语言:txt
复制
 System.out.println(Thread.currentThread().getName()+"结束时间[" + new java.util.Date().getTime());
代码语言:txt
复制
 });
代码语言:txt
复制
 }
代码语言:txt
复制
 }
代码语言:txt
复制
 public static void main(String[] args) {
代码语言:txt
复制
 singleThreadExecutor();
代码语言:txt
复制
 }
代码语言:txt
复制
}

执行结果如下:

90分钟10个手写案例,从源码底层给你讲解7种线程池创建方式

4.ScheduledThreadPool

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

源码

这个线程得源码有一点特殊,由两部分组成,第一部分

90分钟10个手写案例,从源码底层给你讲解7种线程池创建方式

这里创建了ScheduledThreadPoolExecutor,

ScheduledThreadPoolExecutor继承自ThreadPoolExecutor ,主要用于给定 延时之后的运行任务或定期处理任务

第二部分

90分钟10个手写案例,从源码底层给你讲解7种线程池创建方式

使用示例如下:

代码语言:txt
复制
package com.test.thread;
代码语言:txt
复制
import java.util.Date;
代码语言:txt
复制
import java.util.concurrent.Executors;
代码语言:txt
复制
import java.util.concurrent.ScheduledExecutorService;
代码语言:txt
复制
import java.util.concurrent.TimeUnit;
代码语言:txt
复制
/**
代码语言:txt
复制
 * @author :biws
 * @date :Created in 2020/12/17 20:43
 * @description:测试ScheduledThreadPool
 */
public class testThread3 {
 public static void scheduledThreadPool() {
 // 创建线程池
 ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);
 // 添加定时执行任务(1s 后执行)
 for(int i=0;i<4;i++){
 final int index=i;
代码语言:txt
复制
 System.out.println(index+"添加任务,时间:" + new Date()+Thread.currentThread().getName());
代码语言:txt
复制
 threadPool.schedule(() -> {
代码语言:txt
复制
 System.out.println(index+"任务被执行,时间:" + new Date()+Thread.currentThread().getName());
代码语言:txt
复制
 /* try {
代码语言:txt
复制
 TimeUnit.SECONDS.sleep(1);
代码语言:txt
复制
 } catch (InterruptedException e) {
代码语言:txt
复制
 }*/
代码语言:txt
复制
 }, 1, TimeUnit.SECONDS);
代码语言:txt
复制
 }
代码语言:txt
复制
 }
代码语言:txt
复制
 public static void main(String[] args) {
代码语言:txt
复制
 scheduledThreadPool();
代码语言:txt
复制
 }
代码语言:txt
复制
}

执行结果如下:

90分钟10个手写案例,从源码底层给你讲解7种线程池创建方式

5.SingleThreadScheduledExecutor

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

使用示例如下:

代码语言:txt
复制
package com.test.thread;
代码语言:txt
复制
import java.util.Date;
代码语言:txt
复制
import java.util.concurrent.Executors;
代码语言:txt
复制
import java.util.concurrent.ScheduledExecutorService;
代码语言:txt
复制
import java.util.concurrent.TimeUnit;
代码语言:txt
复制
/**
代码语言:txt
复制
 * @author :biws
 * @date :Created in 2020/12/17 20:54
 * @description:测试SingleThreadScheduledExecutor
 */
public class testThread5 {
 public static void SingleThreadScheduledExecutor() {
 // 创建线程池
 ScheduledExecutorService threadPool = Executors.newSingleThreadScheduledExecutor();
 // 添加定时执行任务(2s 后执行)
 for (int i=0;i<4;i++) {
代码语言:txt
复制
 final  int index=i;
代码语言:txt
复制
 System.out.println(index+"添加任务,时间:" + new Date());
代码语言:txt
复制
 threadPool.schedule(() -> {
代码语言:txt
复制
 System.out.println(index+"任务被执行,时间:" + new Date());
代码语言:txt
复制
 try {
代码语言:txt
复制
 TimeUnit.SECONDS.sleep(1);
代码语言:txt
复制
 } catch (InterruptedException e) {
代码语言:txt
复制
 }
代码语言:txt
复制
 }, 2, TimeUnit.SECONDS);
代码语言:txt
复制
 }
代码语言:txt
复制
 }
代码语言:txt
复制
 public static void main(String[] args) {
代码语言:txt
复制
 SingleThreadScheduledExecutor();
代码语言:txt
复制
 }
代码语言:txt
复制
}

执行结果如下:

90分钟10个手写案例,从源码底层给你讲解7种线程池创建方式

从上述结果可以看出,任务在 2 秒之后被执行了,符合我们的预期。

6.newWorkStealingPool

创建一个抢占式执行的线程池(任务执行顺序不确定)

注意:

此方法只有在 JDK 1.8+ 版本中才能使用。

使用示例如下:

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

执行结果如下:

90分钟10个手写案例,从源码底层给你讲解7种线程池创建方式

7.ThreadPoolExecutor

最原始的创建线程池的方式,它包含了 7 个参数可供设置。

使用示例如下:

代码语言:txt
复制
package com.test.thread;
代码语言:txt
复制
import java.util.concurrent.LinkedBlockingQueue;
代码语言:txt
复制
import java.util.concurrent.ThreadPoolExecutor;
代码语言:txt
复制
import java.util.concurrent.TimeUnit;
代码语言:txt
复制
/**
代码语言:txt
复制
 * @author :biws
 * @date :Created in 2020/12/17 21:07
 * @description:测试ThreadPoolExecutor
 */
public class testThread7 {
 public static void myThreadPoolExecutor() {
 // 创建线程池
 ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
代码语言:txt
复制
 TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));
代码语言:txt
复制
 // 执行任务
代码语言:txt
复制
 for (int i = 0; i < 10; i++) {
代码语言:txt
复制
 final int index = i;
代码语言:txt
复制
 threadPool.execute(() -> {
代码语言:txt
复制
 System.out.println(index + " 被执行,线程名:" + Thread.currentThread().getName());
代码语言:txt
复制
 try {
代码语言:txt
复制
 Thread.sleep(1000);
代码语言:txt
复制
 } catch (InterruptedException e) {
代码语言:txt
复制
 e.printStackTrace();
代码语言:txt
复制
 }
代码语言:txt
复制
 });
代码语言:txt
复制
 }
代码语言:txt
复制
 }
代码语言:txt
复制
 public static void main(String[] args) {
代码语言:txt
复制
 myThreadPoolExecutor();
代码语言:txt
复制
 }
代码语言:txt
复制
}

执行结果如下:

90分钟10个手写案例,从源码底层给你讲解7种线程池创建方式

ThreadPoolExecutor 参数介绍

就像我前面説的,ThreadPoolExecutor 是可以设置一些参数的,在我的代碼中,我只设置了这几个参数

90分钟10个手写案例,从源码底层给你讲解7种线程池创建方式

源码 中是这样编写的

代码语言:txt
复制
    public ThreadPoolExecutor(
代码语言:txt
复制
                                                    int corePoolSize,
代码语言:txt
复制
                              int maximumPoolSize,
代码语言:txt
复制
                              long keepAliveTime,
代码语言:txt
复制
                              TimeUnit unit,
代码语言:txt
复制
                              BlockingQueue<Runnable> workQueue) {
代码语言:txt
复制
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
代码语言:txt
复制
             Executors.defaultThreadFactory(), defaultHandler);
代码语言:txt
复制
    }

7 个参数代表的含义如下:

corePoolSize

核心线程数,线程池中始终存活的线程数。

maximumPoolSize

最大线程数,线程池中允许的最大线程数,当线程池的任务队列满了之后可以创建的最大线程数。

keepAliveTime \ unit:

最大线程数可以存活的时间,当线程中没有任务执行时,最大线程就会销毁一部分,最终保持核心线程数量的线程。

单位是和参数 3 存活时间配合使用的,合在一起用于设定线程的存活时间

threadFactory

线程工厂,主要用来创建线程,默认为正常优先级、非守护线程。

workQueue

一个阻塞队列,用来存储线程池等待执行的任务,均为线程安全,它包含以下 7 种类型:

ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。

LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。

SynchronousQueue:一个不存储元素的阻塞队列,即直接提交给线程不保持它们。

PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。

DelayQueue:一个使用优先级队列实现的无界阻塞队列,只有在延迟期满时才能从中提取元素。

LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。与SynchronousQueue类似,还含有非阻塞方法。

LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。

较常用的是 LinkedBlockingQueue 和 Synchronous,线程池的排队策略与 BlockingQueue 有关。

handler

拒绝策略,拒绝处理任务时的策略,系统提供了 4 种可选:

默认策略为 AbortPolicy。

代码演示

代码语言:txt
复制
。。。。。
代码语言:txt
复制
        // 创建线程,线程的任务队列的长度为 1
代码语言:txt
复制
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1,
代码语言:txt
复制
                100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1),
代码语言:txt
复制
                new ThreadPoolExecutor.DiscardPolicy());
代码语言:txt
复制
。。。。

//在Java种共有4种拒绝策略

/*

AbortPolicy:拒绝并抛出异常。

CallerRunsPolicy:使用当前调用的线程来执行此任务。

DiscardOldestPolicy:抛弃队列头部(最旧)的一个任务,并执行当前任务。

DiscardPolicy:忽略并抛弃当前任务。

*/</pre>

演示结果

90分钟10个手写案例,从源码底层给你讲解7种线程池创建方式

我们创建了一个核心线程数和最大线程数都为 1 的线程池,并且给线程池的任务队列设置为 1,这样当我们有 2 个以上的任务时就会触发拒绝策略,

自定义拒绝策略

除了 Java 自身提供的 4 种拒绝策略之外,我们也可以自定义拒绝策略,示例代码如下:

代码语言:txt
复制
。。。
代码语言:txt
复制
        // 创建线程,线程的任务队列的长度为 1
代码语言:txt
复制
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1, 1,
代码语言:txt
复制
                100, TimeUnit.SECONDS, new LinkedBlockingQueue<>(1),
代码语言:txt
复制
                new RejectedExecutionHandler() {
代码语言:txt
复制
                    @Override
代码语言:txt
复制
                    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
代码语言:txt
复制
                        // 执行自定义拒绝策略的相关操作
代码语言:txt
复制
                        System.out.println("我是自定义拒绝策略~");
代码语言:txt
复制
                    }
代码语言:txt
复制
                });
代码语言:txt
复制
。。。

程序的执行结果如下:

90分钟10个手写案例,从源码底层给你讲解7种线程池创建方式

线程池的执行流程

提交一个任务到线程池中,线程池的处理流程如下:

1、判断线程池里的核心线程是否都在执行任务,如果不是(核心线程空闲或者还有核心线程没有被创建)则创建一个新的工作线程来执行任务。如果核心线程都在执行任务,则进入下个流程。

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

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

90分钟10个手写案例,从源码底层给你讲解7种线程池创建方式

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
作者已关闭评论
0 条评论
热度
最新
推荐阅读
目录
  • 1.FixedThreadPool
  • 2.CachedThreadPool
  • 3.SingleThreadExecutor
  • 4.ScheduledThreadPool
  • 5.SingleThreadScheduledExecutor
  • 6.newWorkStealingPool
  • 7.ThreadPoolExecutor
  • ThreadPoolExecutor 参数介绍
  • 自定义拒绝策略
  • 线程池的执行流程
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档