线程池(介绍与使用)

线程池

1. 引子

在多线程环境下,存在这样一种业务场景:需要对线程进行频繁的创建和删除,这种场景是非常消耗我们的系统资源的,对此,我们可以引入线程池来提前创建好线程为我们服务

2. 概念

线程池:字面意思理解就是存放着线程的池子,能够将我们需要使用的线程在池子中提前准备好,需要使用线程时直接从线程池中获取,用完了再重新放回线程池等待被重新调用,减少了我们创建线程和销毁线程的开销,同时因为从线程池中取出和放回线程是纯用户态代码,操作速度比内核操作更快更稳定!!

3. ThreadPoolExecutor类

java标准库为我们提供了一个线程池的api:ThreadPoolExecutor

通过查询java文档可知它有以下四种构造方法:

在这里插入图片描述

这里我们主要讲解一下最后一种,它的参数是最全的:

  • int corePoolSize核心线程数,类似于最小线程数,线程池中最后的底线~

  • int maximumPoolSize最大线程数,可以在核心线程数的基础上继续添加线程,但最终线程数量不能超过最大线程数

  • long keepAliveTime保持存活时间,即除核心线程外的其它线程处于空闲状态时能够存在的最长时间

  • TimeUnit unit:保持存活时间的时间单位,可以为ms,s,min,hour等

  • BlockingQueue<Runnable> workQueue任务队列,用来存放需要执行的任务,这里使用的是阻塞队列,它维护了线程安全以及阻塞功能

  • ThreadFactory threadFactory线程工厂,遵循工厂模式

    工厂模式:一种设计模式,通过工厂类来创建指定的对象,即通过静态方法将new操作封装起来,在方法内部设定不同属性完成对象初始化,构造对象的过程称为工厂模式

    这里的ThreadFactory类封装了操作new Thread的方法,通过这个类来创建线程对象,文档介绍如下:

    在这里插入图片描述

  • RejectedExecutionHandler handler拒绝策略,线程池在执行任务时需要遵循的策略,我们知道线程池中维护了一个任务队列,它存储任务的容量是有限的,当队列的存储容量满了的时候,又有新的任务进来了,对于线程池来说,该怎么做?这个时候它需要遵循以下四个拒绝策略之一:

    在这里插入图片描述

ThreadPoolExecutor类适用于需要高度定制化线程池时使用,而如果只是需要简单使用线程池的话,可以使用Executors

4. Executors类

Executros是标准库提供的一个工厂类,它对ThreadPoolExecutor进行了封装,通过这个类我们可以创建出多个不同的线程池对象

在这里插入图片描述

其中:

  • newFixedThreadPool:固定线程数目的线程池
  • newCachedThreadPool:线程数目可以动态扩容的线程池
  • newSingleThreadExecutor:只能包含单个线程的线程池
  • newScheuledThreadPool:类似于定时器,可创建执行延时任务的线程

代码示例🌰:

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

public class ThreadPoolExecutorTest {

    public static void main(String[] args) {
		// 创建一个固定线程数目的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(5);

        executorService.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello!!");
            }
        });
    }
}

// 执行结果
// Hello!!

拓展

在创建线程池的时候,很多时候都需要我们先确定好要创建线程的数量,这该怎么确定??网络上有很多的说法,如通过CPU的逻辑核心数来确定,但其实对于不同的程序来说,能够设定的线程数量是不同的,更应该结合实际情况来分析,比如看它这个进程中线程是CPU密集型多还是I/O密集型多,当然很多时候一个进程中的线程,一部分是CPU密集型,一部分是I/O密集型,这里的比例无法完全估计,对此,需要我们通过测试实验来找到那个合适的值,尝试给线程池设定不同的线程数目,分别进行性能测试,衡量不同线程数目下进程总的时间开销还有系统资源开销,然后找到两者之间合适的值!!

5. 自定义线程池

java标准库为我们提供了上述的线程池类,我们也可以尝试自己构建一个线程池!!可以按下述四个步骤来:

  1. 提供一个阻塞队列,用来保存需要执行的任务(使用java提供的阻塞队列的话,已经维护好了线程安全与阻塞,这里就不需要重复维护了);
  2. 提供一个列表用来保存创建的线程;
  3. 提供一个构造方法,用来指定创建多少个线程,且每个线程都有执行任务操作的功能(这里我们创建固定线程数目的线程池);
  4. 提供一个submit方法,用来添加任务

代码示例🌰:

class MyThreadPoolExecutor {

    // 用来保存创建的线程
    private List<Thread> threadList = new ArrayList<>();

    // 用来保存需要执行的任务
    private BlockingQueue<Runnable> taskQueue = new ArrayBlockingQueue<>(1000); // 这里限制阻塞队列容量

    // 通过 n 来指定创建的线程数量
   public MyThreadPoolExecutor(int n) {
        for (int i = 0; i < n; i++) {

            Thread t = new Thread(() -> {

                while (true) {
                    try {
                        // 从任务队列中取出任务,这里的阻塞队列已经维护了线程安全与阻塞功能
                        Runnable task = taskQueue.take(); // 当队列为空时会进入阻塞
                        task.run();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
            t.start();
            threadList.add(t);
        }
   }

   public void submit(Runnable task) throws InterruptedException {
       taskQueue.put(task);
   }
}

这样我们就自己创建好了一个线程池!!通过下述代码测试一下:

public class MyThreadPoolExecutorTest {

    public static void main(String[] args) throws InterruptedException {
        
        MyThreadPoolExecutor myThreadPoolExecutor = new MyThreadPoolExecutor(5); // 这里创建5个线程

        for (int i = 0;i < 1000;i++) {
            int n = i; // 这里为了解决变量捕获问题(在lambda中调用外部变量就会产生的问题)
            myThreadPoolExecutor.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println("当前线程为:" + Thread.currentThread().getName() + ", 执行任务:" + n);
                }
            });
        }
    }
}

执行结果

在这里插入图片描述

可以看到我们自己创建的线程池能够成功使用了!!

以上便是线程池的介绍与使用流程了,如果这些内容对大家有帮助的话请给一个三连关注吧💕( •̀ ω •́ )✧( •̀ ω •́ )✧✨

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值