手把手入门学习:Java线程池(代码详解)

简介: 本篇整理了线程池相关的代码案例,整理了好几天才实践完这篇文章,也希望自己对线程池有更深入的理解,欢迎阅读学习交流,分享获取新知,大家一起进步!

1、Preface序言

线程(英语:thread)是操作系统能够进行运算调度的最小单位。

线程池就是创建若干个可执行的线程放入一个池(容器)中,有任务需要处理时,会提交到线程池中的任务队列,处理完之后线程并不会被销毁,而是仍然在线程池中等待下一个任务。

线程池能有效的处理多个线程的并发问题,避免大量的线程因为互相强占系统资源导致阻塞现象,能够有效的降低频繁创建和销毁线程对性能所带来的开销。


Executors线程工厂类一共可以创建四种类型的线程池,通过Executors.newXXX即可创建。

ThreadPoolExecutor提供了四个构造函数

//五个参数的构造函数
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue)

//六个参数的构造函数-1
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory)

//六个参数的构造函数-2
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler)

//七个参数的构造函数
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

在Java中,线程池的概念是Executor这个接口,具体实现为ThreadPoolExecutor类,学习Java中的线程池,就是对ThreadPoolExecutor构造函数的参数的配置和学习。

int corePoolSize:该线程池中核心线程数最大值

int maximumPoolSize: 该线程池中线程总数最大值

long keepAliveTime:该线程池中非核心线程闲置超时时长

TimeUnit unit:keepAliveTime的单位

BlockingQueue workQueue:该线程池中的任务队列:维护着等待执行的Runnable对象

在这里插入图片描述
接下来用代码案例来真实展示常见线程池的创建过程以及如何运用,看一千次不如实践一次,try!!


2、FixedThreadPool

可重用固定线程数的线程池

newFixedThreadPool:创建一个定长的线程池,可控制线程的最大并发数,超出的线程在队列中等待。

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

在这里插入图片描述

有指定的线程数的线程池,有核心的线程,里面有固定的线程数量,响应的速度快。
在这里插入图片描述

核心线程是没有超时机制的,队列大小没有限制,除非线程池关闭了核心线程才会被回收。

/**
 * Created by 辰兮 2020/10/10
 * newFixedThreadPool 创建一个固定长度线程池,可控制线程最大并发数,超出的线程会在队列中等待。
 */
public class NewFixedThreadPool {
    public static void main(String[] args) {
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
        for (int i = 0; i < 5; i++)
        {
            final int index = i;

            //1- 在未来某个时间执行给定的命令。
            // 该命令可能在新的线程、已入池的线程或者正调用的线程中执行,这由 Executor 实现决定。
            fixedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    threadRunMethod(index);
                }
            });

            //2- 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。
            // 该 Future 的 get 方法在成功完成时将会返回给定的结果
            fixedThreadPool.submit(new Runnable() {
                @Override
                public void run() {
                    threadRunMethod(index);
                }
            });
        }
        fixedThreadPool.shutdown();
    }

    /**
     *
     * @param index
     */
    private static void threadRunMethod(int index) {
        try {
            System.out.println(index);
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
0
0
1
1
2
2
3
3
4
4

Process finished with exit code 0

3、CachedThreadPool

可缓存的线程池(ps:速度快)

CachedThreadPool 缓存线程池,只有非核心线程,最大线程数很大(Int.Max(values)),它会为每一个任务添加一个新的线程,这边有一个超时机制,当空闲的线程超过60s内没有用到的话,就会被回收。缺点就是没有考虑到系统的实际内存大小。

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

newCachedThreadPool:创建一个可缓存的线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

/**
 * Created by 辰兮 2020/10/10
 * newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,
 * 若无可回收,则新建线程。线程池的规模不存在限制。
 */
public class NewCachedThreadPool {
    public static void main(String[] args) {
        ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
        for (int i = 0; i < 5; i++) {
            final int index = i;
            try {
                Thread.sleep(index * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //1- 在未来某个时间执行给定的命令。
            // 该命令可能在新的线程、已入池的线程或者正调用的线程中执行,这由 Executor 实现决定。
            cachedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(index);
                }
            });

            //2- 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。
            // 该 Future 的 get 方法在成功完成时将会返回给定的结果
            cachedThreadPool.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(index);
                }
            });
        }
        cachedThreadPool.shutdown();
    }
}
0
0
1
1
2
2
3
3
4
4

Process finished with exit code 0

4、SingleThreadExecutor

单线程线程池(ps:速度相对较慢)

通过指定的顺序将任务一个个丢到线程,都乖乖的排队等待执行,不处理并发的操作,不会被回收。干活效率慢。

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

它只会创建一条工作线程处理任务;

采用的阻塞队列为LinkedBlockingQueue;

/**
 * Created by 辰兮 2020.10.10
 * newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,
 * 保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
 */
public class NewSingleThreadExecutor {
    public static void main(String[] args) {

        ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 5; i++) {
            final int index = i;
            /*singleThreadExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println("newSingleThreadExecutor: " + index);
                        Thread.sleep(2*1000);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });*/

            singleThreadExecutor.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println("newSingleThreadExecutor: " + index);
                        Thread.sleep(1*1000);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }

        singleThreadExecutor.shutdown();
    }

}
newSingleThreadExecutor: 0
newSingleThreadExecutor: 1
newSingleThreadExecutor: 2
newSingleThreadExecutor: 3
newSingleThreadExecutor: 4

Process finished with exit code 0

5、ScheduledThreadPool

调度线程池

它用来处理延时任务或定时任务。

    /**
     * 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, TimeUnit.NANOSECONDS,
              new DelayedWorkQueue());
    }

这个线程池是唯一具有延迟执行和周期重复执行的线程池。它的核心线程池固定,非核心线程的数量没有限制,但是闲置时会立即会被回收。

/**
 * Created by 辰兮 2020/10/10
 * newScheduledThreadPool 创建一个固定长度线程池,支持定时及周期性任务执行。
 */
public class NewScheduledThreadPool {
    public static void main(String[] args) {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5);

        //testSchedule(scheduledExecutorService);

        //testScheduleAtFixedRate(scheduledExecutorService);

        testScheduleWithFixedDelay(scheduledExecutorService);

        // 终止线程池
        //scheduledExecutorService.shutdown();
    }

    /**
     *
     * 跟 testScheduleAtFixedRate 非常类似,就是延迟的时间有点区别
     * 创建并执行一个在给定初始延迟后首次启用的定期操作,后续操作具有给定的周期;
     * 也就是将在 initialDelay 后开始执行,然后在 initialDelay+period 后执行,
     * 接着在 initialDelay + 2 * period 后执行,依此类推。
     *
     * 如果任务里面执行的时间大于 period 的时间,下一次的任务会推迟执行。
     * 推迟的时间 : 等到上次的任务执行完之后再延迟period 的时间后执行。
     * @param scheduledExecutorService
     */
    private static void testScheduleWithFixedDelay(ScheduledExecutorService scheduledExecutorService) {
        scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("延迟2秒,再3秒执行一次");
                    //如果任务里面执行的时间大于 period 的时间,下一次的任务会推迟执行。
                    //本次任务执行完后下次的任务还需要延迟period时间后再执行
                    Thread.sleep(6*1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },2,3,TimeUnit.SECONDS);
    }

    /**
     * 创建并执行一个在给定初始延迟后首次启用的定期操作,后续操作具有给定的周期;
     * 也就是将在 initialDelay 后开始执行,然后在 initialDelay+period 后执行,
     * 接着在 initialDelay + 2 * period 后执行,依此类推。
     *
     * 如果任务里面执行的时间大于 period 的时间,下一次的任务会推迟执行。
     * 推迟的时间 : 等到上次的任务执行完就立马执行。
     * @param scheduledExecutorService
     */
    private static void testScheduleAtFixedRate(ScheduledExecutorService scheduledExecutorService) {
        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("延迟2秒,再3秒执行一次");
                    //如果任务里面执行的时间大于 period 的时间,下一次的任务会推迟执行。
                    //如果任务里面执行的时间大于 period 的时间,本次任务执行完后,下次任务立马执行。
                    Thread.sleep(6*1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        },2,3,TimeUnit.SECONDS);
    }

    /**
     * 创建并执行在给定延迟后启用的一次性操作
     * @param scheduledExecutorService
     */
    private static void testSchedule(ScheduledExecutorService scheduledExecutorService) {
        scheduledExecutorService.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("delay 3 seconds");
            }
        }, 3, TimeUnit.SECONDS);
    }
}
延迟2秒,再3秒执行一次
延迟2秒,再3秒执行一次
延迟2秒,再3秒执行一次
延迟2秒,再3秒执行一次
延迟2秒,再3秒执行一次
延迟2秒,再3秒执行一次
延迟2秒,再3秒执行一次
....

线程池的好处

1、降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

2、提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

3、提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源。


阿里巴巴Java手册:为什么不建议使用Executors创建线程池?

了。
不建议采用Executors创建线程池,但是可以自定义线程池相关参数创建!


第一次整理关于线程池相关知识,如有不对欢迎大佬们指导!


The best investment is to invest in yourself.

愿你们奔赴在自己的热爱里!

目录
相关文章
|
3天前
|
监控 安全 Java
在 Java 中使用线程池监控以及动态调整线程池时需要注意什么?
【10月更文挑战第22天】在进行线程池的监控和动态调整时,要综合考虑多方面的因素,谨慎操作,以确保线程池能够高效、稳定地运行,满足业务的需求。
71 38
|
2天前
|
XML 安全 Java
Java反射机制:解锁代码的无限可能
Java 反射(Reflection)是Java 的特征之一,它允许程序在运行时动态地访问和操作类的信息,包括类的属性、方法和构造函数。 反射机制能够使程序具备更大的灵活性和扩展性
15 5
Java反射机制:解锁代码的无限可能
|
3天前
|
存储 安全 Java
系统安全架构的深度解析与实践:Java代码实现
【11月更文挑战第1天】系统安全架构是保护信息系统免受各种威胁和攻击的关键。作为系统架构师,设计一套完善的系统安全架构不仅需要对各种安全威胁有深入理解,还需要熟练掌握各种安全技术和工具。
27 10
|
3天前
|
Prometheus 监控 Cloud Native
JAVA线程池监控以及动态调整线程池
【10月更文挑战第22天】在 Java 中,线程池的监控和动态调整是非常重要的,它可以帮助我们更好地管理系统资源,提高应用的性能和稳定性。
24 4
|
5天前
|
搜索推荐 Java 数据库连接
Java|在 IDEA 里自动生成 MyBatis 模板代码
基于 MyBatis 开发的项目,新增数据库表以后,总是需要编写对应的 Entity、Mapper 和 Service 等等 Class 的代码,这些都是重复的工作,我们可以想一些办法来自动生成这些代码。
16 6
|
1天前
|
JavaScript Java 项目管理
Java毕设学习 基于SpringBoot + Vue 的医院管理系统 持续给大家寻找Java毕设学习项目(附源码)
基于SpringBoot + Vue的医院管理系统,涵盖医院、患者、挂号、药物、检查、病床、排班管理和数据分析等功能。开发工具为IDEA和HBuilder X,环境需配置jdk8、Node.js14、MySQL8。文末提供源码下载链接。
|
3天前
|
Java 数据处理 开发者
Java多线程编程的艺术:从入门到精通####
【10月更文挑战第21天】 本文将深入探讨Java多线程编程的核心概念,通过生动实例和实用技巧,引导读者从基础认知迈向高效并发编程的殿堂。我们将一起揭开线程管理的神秘面纱,掌握同步机制的精髓,并学习如何在实际项目中灵活运用这些知识,以提升应用性能与响应速度。 ####
19 3
|
3天前
|
Prometheus 监控 Cloud Native
在 Java 中,如何使用线程池监控以及动态调整线程池?
【10月更文挑战第22天】线程池的监控和动态调整是一项重要的任务,需要我们结合具体的应用场景和需求,选择合适的方法和策略,以确保线程池始终处于最优状态,提高系统的性能和稳定性。
35 2
|
5天前
|
Java
Java中的多线程编程:从入门到精通
本文将带你深入了解Java中的多线程编程。我们将从基础概念开始,逐步深入探讨线程的创建、启动、同步和通信等关键知识点。通过阅读本文,你将能够掌握Java多线程编程的基本技能,为进一步学习和应用打下坚实的基础。
|
6天前
|
Java
通过Java代码解释成员变量(实例变量)和局部变量的区别
本文通过一个Java示例,详细解释了成员变量(实例变量)和局部变量的区别。成员变量属于类的一部分,每个对象有独立的副本;局部变量则在方法或代码块内部声明,作用范围仅限于此。示例代码展示了如何在类中声明和使用这两种变量。