手把手入门学习: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.

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

目录
相关文章
|
2天前
|
设计模式 Java
Java设计模式:组合模式的介绍及代码演示
组合模式是一种结构型设计模式,用于将多个对象组织成树形结构,并统一处理所有对象。例如,统计公司总人数时,可先统计各部门人数再求和。该模式包括一个通用接口、表示节点的类及其实现类。通过树形结构和节点的通用方法,组合模式使程序更易扩展和维护。
Java设计模式:组合模式的介绍及代码演示
|
4天前
|
设计模式 架构师 Java
Java开发工程师转架构师需要学习什么
Java开发工程师转型为架构师需掌握多项技能:精通Java及框架、数据库与分布式系统;熟悉设计模式与架构模式;积累项目经验;提升沟通与领导力;持续学习新技术;培养系统设计与抽象能力;了解中间件及开发工具;并注重个人特质与职业发展。具体路径应结合个人目标与实际情况制定。
34 18
|
17天前
|
监控 Java 调度
【Java学习】多线程&JUC万字超详解
本文详细介绍了多线程的概念和三种实现方式,还有一些常见的成员方法,CPU的调动方式,多线程的生命周期,还有线程安全问题,锁和死锁的概念,以及等待唤醒机制,阻塞队列,多线程的六种状态,线程池等
79 6
【Java学习】多线程&JUC万字超详解
|
2天前
|
Java 程序员 API
Java中的Lambda表达式:简化代码的秘密武器
在Java 8中引入的Lambda表达式是一种强大的编程工具,它可以显著简化代码,提高可读性。本文将介绍Lambda表达式的基本概念、优势以及在实际开发中的应用。通过具体示例,您将了解如何使用Lambda表达式来简化集合操作、线程编程和函数式编程。让我们一起探索这一革命性的特性,看看它是如何改变Java编程方式的。
15 4
|
2天前
|
Java 开发者
探索Java中的Lambda表达式:简化你的代码
【8月更文挑战第49天】在Java 8的发布中,Lambda表达式无疑是最令人兴奋的新特性之一。它不仅为Java开发者提供了一种更加简洁、灵活的编程方式,而且还极大地提高了代码的可读性和开发效率。本文将通过实际代码示例,展示如何利用Lambda表达式优化和重构Java代码,让你的编程之旅更加轻松愉快。
|
10天前
|
Java 调度 开发者
Java并发编程:深入理解线程池
在Java的世界中,线程池是提升应用性能、实现高效并发处理的关键工具。本文将深入浅出地介绍线程池的核心概念、工作原理以及如何在实际应用中有效利用线程池来优化资源管理和任务调度。通过本文的学习,读者能够掌握线程池的基本使用技巧,并理解其背后的设计哲学。
|
10天前
|
缓存 监控 Java
Java中的并发编程:理解并应用线程池
在Java的并发编程中,线程池是提高应用程序性能的关键工具。本文将深入探讨如何有效利用线程池来管理资源、提升效率和简化代码结构。我们将从基础概念出发,逐步介绍线程池的配置、使用场景以及最佳实践,帮助开发者更好地掌握并发编程的核心技巧。
|
7天前
|
SQL JavaScript 前端开发
基于Java访问Hive的JUnit5测试代码实现
根据《用Java、Python来开发Hive应用》一文,建立了使用Java、来开发Hive应用的方法,产生的代码如下
28 6
|
13天前
|
存储 Java 开发者
【Java新纪元启航】JDK 22:解锁未命名变量与模式,让代码更简洁,思维更自由!
【9月更文挑战第7天】JDK 22带来的未命名变量与模式匹配的结合,是Java编程语言发展历程中的一个重要里程碑。它不仅简化了代码,提高了开发效率,更重要的是,它激发了我们对Java编程的新思考,让我们有机会以更加自由、更加创造性的方式解决问题。随着Java生态系统的不断演进,我们有理由相信,未来的Java将更加灵活、更加强大,为开发者们提供更加广阔的舞台。让我们携手并进,共同迎接Java新纪元的到来!
41 11
|
5天前
|
Java 开发者
探索Java中的Lambda表达式:简化代码,提升效率
【9月更文挑战第14天】本文旨在揭示Java 8中引入的Lambda表达式如何革新了我们编写和管理代码的方式。通过简洁明了的语言和直观的代码示例,我们将一起走进Lambda表达式的世界,了解其基本概念、语法结构以及在实际编程中的应用。文章不仅会展示Lambda表达式的魅力所在,还会指导读者如何在日常工作中有效利用这一特性,以提高编码效率和程序可读性。