JavaEE 初阶篇-深入了解线程池(线程池创建、线程池如何处理任务)

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新网络安全全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上网络安全知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注网络安全)
img

正文

1.2 不使用线程池的问题

1.3 线程池的工作原理图

1.4 如何创建线程池?

2.0 通过 ThreadPoolExecutor 类自定义创建线程池

2.1 ThreadPoolExecutor 构造器

3.0 线程池处理 Runnable 任务

3.1 通过 void execute(Runnable command) 方法

3.2 通过 void shutdown() 方法

3.3 通过 List shutdownNow() 方法

3.4 临时线程什么时候创建?

3.5 什么时候会开始拒绝新任务?

4.0 线程池处理 Callable 任务

4.1 通过 submit() 方法

5.0 通过 Executors 工具类创建出线程池

5.1 通过 Executors.newFixedThreadPool(int nThreads) 方法来创建线程池

5.2 通过 Executors.newCachedThreadPool() 方法来创建线程池

5.3 通过 Eexcutors.newSingleThreadExecutor() 方法来创建线程池

6.0 新任务拒绝策略

6.1 AbortPolicy(默认策略,直接抛出异常)

6.2 DiscardPolicy(直接丢弃任务)

6.3 CallerRunsPolicy(由调用线程执行任务)

6.4 DiscardOldestPolicy(丢弃队列中最旧的任务)


1.0 线程池概述

线程池是一种重要的并发编程机制,用于管理和复用线程,以提高应用程序的性能和资源利用率。

线程池就是一个可以复用线程的技术。

简单来说,线程池起到了复用线程的作用。假如不用线程池的话,用线程来处理任务,来多少任务就要创建多少了线程,这就会导致线程堆积,而且线程的创建和销毁虽然比进程的消耗低,但是对于大量的线程进行创建和销毁,就会导致消耗更大了,这里的消耗主要是指 CPU 消耗。还有一方面的原因,如果手动创建线程的话,需要通过内核进行创建;若在线程池中取出线程,不需要通过内核,直接通过用户态即可。所以总结来说,线程池中的线程一次创建完毕之后,不执行任务的时候,核心线程就存放在线程池中,临时线程会有一个时间的存放,超过了规定的时间就会自动销毁;需要执行任务的时候,就会从线程池中取出线程,此时不需要通过内核态,直接是通过用户态。用完之后,将线程放回到线程池中,等待下一个任务。

1.1 线程池的优点

1)降低线程创建和销毁的开销。通过重用线程从而减少频繁创建和开销线程所带来的性能损耗。

2)控制并发线程数量。通过设定线程池的大小和配置,可以限制并发线程的数量,避免系统资源耗尽。

1.2 不使用线程池的问题

假设用户发起一个请求,后台就需要创建一个新线程来处理,下次新任务来了肯定又要创建新线程处理的,而创建新线程的开销是很大的,并且请求过多时,肯定会产生大量的线程出来,这样会严重影响系统的性能。

1.3 线程池的工作原理图

1.4 如何创建线程池?

常见的创建线程池的方式有两种:

1)通过已经实现 ExecutorService 接口的 ThreadPoolExecutor 类来创建出线程池。

2)通过 Executors 工具类创建出线程池。

2.0 通过 ThreadPoolExecutor 类自定义创建线程池

可以直接使用 ThreadPoolExecutor 类来创建自定义的线程池,通过指定核心线程数、最大线程数、工作队列等参数来满足特定的需求。这种方法更灵活,可以根据具体场景进行定制化配置。

2.1 ThreadPoolExecutor 构造器

1)参数一:corePoolSize:指定线程池的核心线程的数量。

2)参数二:maximumPoolSize:指定线程池的最大线程数量。

3)参数三:keepAliveTime:指定临时线程的存活时间。

4)参数四:unit:指定临时线程的存活时间单位(秒、分、时、天)。

5)参数五:workQueue:指定线程池的任务队列。

6)参数六:threadFactory:指定线程池的线程工厂(创建线程的地方)。

7)参数七:handler:指定线程池的任务拒绝策略(线程都在忙,任务队列也满的时候,新任务来了该怎么处理)。

具体创建线程池代码如下:

import java.util.concurrent.*;
public class CreateThreadPoolExecutor {

    public Executor threadPool = new ThreadPoolExecutor(
            3,//核心线程数量
            5,//最大线程数量
            8,//临时线程存活时间
            TimeUnit.SECONDS,//临时线程存活时间单位
            new ArrayBlockingQueue<>(4),//创建任务队列,最大存放任务数量为4
            Executors.defaultThreadFactory(),//线程工厂,
            //用到了Executors工具类来创建默认线程工厂
            new ThreadPoolExecutor.AbortPolicy());//拒绝策略,默认策略:抛出异常
    
}

3.0 线程池处理 Runnable 任务

线程池通过执行 Runnable 任务来实现多线程处理。将实现 Runnable 接口的任务提交给线程池,线程池会根据配置的参数调度任务执行。核心线程数内的任务会立即执行,超出核心线程数的任务会被放入任务队列中。如果任务队列已满,且线程数未达到最大线程数,线程池会创建新线程执行任务。线程池管理线程的生命周期,重用线程以减少线程创建和销毁的开销。

ExecutorService 的常用方法:

3.1 通过 void execute(Runnable command) 方法

将 Runnable 类型的任务交给线程池,线程池就会自动创建线程,自动执行 run() 方法且自动启动线程。

代码如下:

import java.util.concurrent.*;
public class CreateThreadPoolExecutor {
    public static void main(String[] args) {
        Executor threadPool = new ThreadPoolExecutor(3,//核心线程数量
                5,//最大线程数量
                8,//临时线程存活时间
                TimeUnit.SECONDS,//临时线程存活时间单位
                new ArrayBlockingQueue<>(4),//创建任务队列,最大存放任务数量为4
                Executors.defaultThreadFactory(),//线程工厂,
                //用到了Executors工具类来创建默认线程工厂
                new ThreadPoolExecutor.AbortPolicy());//拒绝策略,默认策略:抛出异常

        //定义出Runnable类型的任务
        MyRunnable myRunnable1 = new MyRunnable();
        MyRunnable myRunnable2 = new MyRunnable();
        //将该任务提交给线程池
        threadPool.execute(myRunnable1);
        threadPool.execute(myRunnable2);
        
    }
}
class  MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "正在执行");
    }
}

运行结果:

需要注意的是,任务执行完毕之后,线程池中的线程不会销毁,还在继续运行中。

3.2 通过 void shutdown() 方法

等待全部任务执行完毕后,再关闭线程池。想要手动关闭线程池中的线程,可以用该方法,等待全部任务都执行后才会关闭。

代码如下:

import java.util.concurrent.*;
public class CreateThreadPoolExecutor {
    public static void main(String[] args) {
        Executor threadPool = new ThreadPoolExecutor(3,//核心线程数量
                5,//最大线程数量
                8,//临时线程存活时间
                TimeUnit.SECONDS,//临时线程存活时间单位
                new ArrayBlockingQueue<>(4),//创建任务队列,最大存放任务数量为4
                Executors.defaultThreadFactory(),//线程工厂,
                //用到了Executors工具类来创建默认线程工厂
                new ThreadPoolExecutor.AbortPolicy());//拒绝策略,默认策略抛出异常

        //定义出Runnable类型的任务
        MyRunnable myRunnable1 = new MyRunnable();
        MyRunnable myRunnable2 = new MyRunnable();
        //将该任务提交给线程池
        threadPool.execute(myRunnable1);
        threadPool.execute(myRunnable2);

        //当全部任务都执行结束后,才会关闭
        ((ThreadPoolExecutor) threadPool).shutdown();

    }
}
class  MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "正在执行");
    }
}

运行结果:

3.3 通过 List shutdownNow() 方法

立刻关闭线程池,停止正在执行的任务,并返回队列中未执行的任务。先比较与以上的方法,当前的方法就很“激进”了。不管任务结束与否,都会关闭线程池中的线程。

代码如下:

import java.util.List;
import java.util.concurrent.*;
public class CreateThreadPoolExecutor {
    public static void main(String[] args) {
        Executor threadPool = new ThreadPoolExecutor(
            3,//核心线程数量
            5,//最大线程数量
            8,//临时线程存活时间
            TimeUnit.SECONDS,//临时线程存活时间单位
            new ArrayBlockingQueue<>(4),//创建任务队列,最大存放任务数量为4
            Executors.defaultThreadFactory(),//线程工厂,
            //用到了Executors工具类来创建默认线程工厂
            new ThreadPoolExecutor.AbortPolicy());//拒绝策略,默认策略:抛出异常

        //定义出Runnable类型的任务
        MyRunnable myRunnable1 = new MyRunnable();
        MyRunnable myRunnable2 = new MyRunnable();
        MyRunnable myRunnable3 = new MyRunnable();
        MyRunnable myRunnable4 = new MyRunnable();
        MyRunnable myRunnable5 = new MyRunnable();
        //将该任务提交给线程池
        threadPool.execute(myRunnable1);
        threadPool.execute(myRunnable2);
        threadPool.execute(myRunnable3);
        threadPool.execute(myRunnable4);
        threadPool.execute(myRunnable5);
        //还没执行完的任务会交给l链表
        List<Runnable> l = ((ThreadPoolExecutor) threadPool).shutdownNow();
        for (int i = 0; i < l.size(); i++) {
            System.out.println("还没来得及执行的任务:" + l.get(i));
        }
    }
}
class  MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "正在执行");
    }
}

运行结果:

3.4 临时线程什么时候创建?

新任务提交时发现核心线程都在忙,任务队列也满了,并且还可以创建临时线程,此时才会创建临时线程。

代码如下:

import java.util.concurrent.*;
public class T {
    public static void main(String[] args) {
        Executor threadPool = new ThreadPoolExecutor(3,//核心线程数量
                5,//最大线程数量
                8,//临时线程存活时间
                TimeUnit.SECONDS,//临时线程存活时间单位
                new ArrayBlockingQueue<>(4),//创建任务队列,最大存放任务数量为4
                Executors.defaultThreadFactory(),//线程工厂,
                //用到了Executors工具类来创建默认线程工厂
                new ThreadPoolExecutor.AbortPolicy());//拒绝策略,默认策略抛出异常

        //定义出Runnable类型的任务,此时该三个任务都被核心线程正在处理
        MyRunnable myRunnable1 = new MyRunnable();
        MyRunnable myRunnable2 = new MyRunnable();
        MyRunnable myRunnable3 = new MyRunnable();
        //这里还没到临时线程创建的时机,被放入到任务队列中等待被核心线程执行
        MyRunnable myRunnable4 = new MyRunnable();
        MyRunnable myRunnable5 = new MyRunnable();
        MyRunnable myRunnable6 = new MyRunnable();
        MyRunnable myRunnable7 = new MyRunnable();

        //此时到了临时线程创建的时机了,核心线程都在满,队列已经满了
        //创建出第一个临时线程
        MyRunnable myRunnable8 = new MyRunnable();
        //创建出第二个临时线程
        MyRunnable myRunnable9 = new MyRunnable();
        
        //将该任务提交给线程池
        threadPool.execute(myRunnable1);
        threadPool.execute(myRunnable2);
        threadPool.execute(myRunnable3);
        threadPool.execute(myRunnable4);
        threadPool.execute(myRunnable5);
        threadPool.execute(myRunnable6);
        threadPool.execute(myRunnable7);
        threadPool.execute(myRunnable8);
        threadPool.execute(myRunnable9);

    }
}

class  MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "==> 
                                                正在执行" + this);
        try {
            //在这里等待10s
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

运行结果:

当前的核心线程为 3 ,最大线程为 5 且任务队列有四个位置。当核心线程达到 3 时,且任务队列满 4 个了,还能创建临时线程时提交新任务就可以创建出临时线程了。

3.5 什么时候会开始拒绝新任务?

核心线程和临时线程都在忙,任务队列也满了,新的任务过来的时候才会开始拒绝任务。

代码如下:

import java.util.concurrent.*;
public class T {
    public static void main(String[] args) {
        Executor threadPool = new ThreadPoolExecutor(3,//核心线程数量
                5,//最大线程数量
                8,//临时线程存活时间
                TimeUnit.SECONDS,//临时线程存活时间单位
                new ArrayBlockingQueue<>(4),//创建任务队列,最大存放任务数量为4
                Executors.defaultThreadFactory(),//线程工厂,
                //用到了Executors工具类来创建默认线程工厂
                new ThreadPoolExecutor.AbortPolicy());//拒绝策略,默认策略抛出异常

        //定义出Runnable类型的任务,此时该三个任务都被核心线程正在处理
        MyRunnable myRunnable1 = new MyRunnable();
        MyRunnable myRunnable2 = new MyRunnable();
        MyRunnable myRunnable3 = new MyRunnable();
        //这里还没到临时线程创建的时机,被放入到任务队列中等待被核心线程执行
        MyRunnable myRunnable4 = new MyRunnable();
        MyRunnable myRunnable5 = new MyRunnable();
        MyRunnable myRunnable6 = new MyRunnable();
        MyRunnable myRunnable7 = new MyRunnable();

        //此时到了临时线程创建的时机了,核心线程都在满,队列已经满了
        //创建出第一个临时线程
        MyRunnable myRunnable8 = new MyRunnable();
        //创建出第二个临时线程
        MyRunnable myRunnable9 = new MyRunnable();

        //此时核心线程都在忙,且队列已经占满了,再提交新任务的时候,
        //就会采取拒绝策略
        MyRunnable myRunnable10 = new MyRunnable();

        //将该任务提交给线程池
        threadPool.execute(myRunnable1);
        threadPool.execute(myRunnable2);
        threadPool.execute(myRunnable3);
        threadPool.execute(myRunnable4);
        threadPool.execute(myRunnable5);
        threadPool.execute(myRunnable6);
        threadPool.execute(myRunnable7);
        threadPool.execute(myRunnable8);
        threadPool.execute(myRunnable9);
        threadPool.execute(myRunnable10);

    }
}

class  MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "==> 
                                                    正在执行" + this);
        try {
            //在这里等待10s
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

运行结果:

拒绝策略默认是抛出异常

4.0 线程池处理 Callable 任务

处理Callable任务时,线程池可通过 submit() 方法提交 Callable 实例,并返回代表任务执行情况的 Future 。通过 Future 可以获取任务执行结果或处理异常。

ExecutorService 常见的方法:

4.1 通过 submit() 方法

提交 Callable 类型的任务给线程池,线程池会选择可用线程、自动执行 call() 方法、自动启动线程。

代码如下:

import java.util.concurrent.*;

public class MyCreateThreadPool {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        ExecutorService executor = new ThreadPoolExecutor(
                3,
                5,
                8,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(4),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

        //创建出Callable类型的实例对象
        MyCallable callable1 = new MyCallable(100);
        MyCallable callable2 = new MyCallable(200);
        MyCallable callable3 = new MyCallable(300);
        MyCallable callable4 = new MyCallable(400);

        //将任务提交到线程池中
        Future<String> f1 = executor.submit(callable1);
        Future<String> f2 = executor.submit(callable2);
        Future<String> f3 = executor.submit(callable3);
        Future<String> f4 = executor.submit(callable4);

        //拿到结果
        System.out.println(f1.get());
        System.out.println(f2.get());
        System.out.println(f3.get());
        System.out.println(f4.get());

    }
}

运行结果:

5.0 通过 Executors 工具类创建出线程池

Executors 工具类提供了创建不同类型线程池的方法,如 newFixedThreadPool 、 newCachedThreadPool、newSingleThreadExecutor 等。这些方法返回不同配置的线程池实例,可用于执行 Runnable 和 Callable 任务。通过 Executors 创建线程池,可方便地管理线程池的大小、任务队列、线程工厂等参数。注意合理选择线程池类型以满足任务需求,避免资源浪费或任务阻塞。

简单来说,Executors 工具类为我们提供了不同特点的线程池。

5.1 通过 Executors.newFixedThreadPool(int nThreads) 方法来创建线程池

创建固定线程数量的线程池,如果某个线程因执行异常而结束,那么线程池会补充一个新线程代替它。

代码如下:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class demo1 {
    public static void main(String[] args) {

        //固定的线程池核心线程为3,临时线程为0,所以最大线程数量也就是3。
        ExecutorService pool = Executors.newFixedThreadPool(3);

        pool.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + 
                                                        "==> 正在执行");
            }
        });
        pool.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + 
                                                        "==> 正在执行");
            }
        });
        pool.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + 
                                                        "==> 正在执行");
            }
        });
        pool.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + 
                                                        "==> 正在执行");
            }
        });
        
    }
}

运行结果:

其实 Executors.newFixedThreadPool(int nThreads) 方法的底层就是通过 ThreadPoolExecutor 类来实现创建线程池的。传入的参数就是核心线程线程数量,也为最大线程数量,因此临时线程数量为 0 。因此没有临时线程的存在,那么该线程池中的线程很固定。

如图:

5.2 通过 Executors.newCachedThreadPool() 方法来创建线程池

线程数量随着任务增加而增加,如果线程任务执行完毕且空闲了 60 s则会被回收掉。也就是有多少了任务线程池会根据任务数量动态创建新线程。

代码如下:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

public class demo2 {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(() -> System.out.println(Thread.currentThread().getName() + " ==> 正在执行"));

        executorService.execute(() -> System.out.println(Thread.currentThread().getName() + " ==> 正在执行"));

        executorService.execute(() -> System.out.println(Thread.currentThread().getName() + " ==> 正在执行"));

        executorService.execute(() -> System.out.println(Thread.currentThread().getName() + " ==> 正在执行"));

        executorService.execute(() -> System.out.println(Thread.currentThread().getName() + " ==> 正在执行"));

        System.out.println( ((ThreadPoolExecutor)executorService).getPoolSize());
    }
}

通过 getPoolSize() 方法可以拿到当前线程池中线程的数量。

运行结果如下:

写在最后

在结束之际,我想重申的是,学习并非如攀登险峻高峰,而是如滴水穿石般的持久累积。尤其当我们步入工作岗位之后,持之以恒的学习变得愈发不易,如同在茫茫大海中独自划舟,稍有松懈便可能被巨浪吞噬。然而,对于我们程序员而言,学习是生存之本,是我们在激烈市场竞争中立于不败之地的关键。一旦停止学习,我们便如同逆水行舟,不进则退,终将被时代的洪流所淘汰。因此,不断汲取新知识,不仅是对自己的提升,更是对自己的一份珍贵投资。让我们不断磨砺自己,与时代共同进步,书写属于我们的辉煌篇章。

需要完整版PDF学习资源私我

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注网络安全)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

ThreadPoolExecutor)executorService).getPoolSize());

}

}


**通过 getPoolSize() 方法可以拿到当前线程池中线程的数量。**


**运行结果如下:**


![](https://img-blog.csdnimg.cn/direct/0a688f9effae4ddeb2e6ce00670f9443.png)


写在最后

在结束之际,我想重申的是,学习并非如攀登险峻高峰,而是如滴水穿石般的持久累积。尤其当我们步入工作岗位之后,持之以恒的学习变得愈发不易,如同在茫茫大海中独自划舟,稍有松懈便可能被巨浪吞噬。然而,对于我们程序员而言,学习是生存之本,是我们在激烈市场竞争中立于不败之地的关键。一旦停止学习,我们便如同逆水行舟,不进则退,终将被时代的洪流所淘汰。因此,不断汲取新知识,不仅是对自己的提升,更是对自己的一份珍贵投资。让我们不断磨砺自己,与时代共同进步,书写属于我们的辉煌篇章。

需要完整版PDF学习资源私我

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注网络安全)
[外链图片转存中…(img-zpACq5g5-1713333845525)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 11
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值