【线程池】java线程池(通俗易懂!!最强详解!!)

目录

了解线程池

什么是线程池?

线程池的优势

线程池的参数

工作原理图

流程描述

线程池应用——实现读取指定路径下的前多少个文件夹下文件的数量

先把完整的代码贴到这里

任务类解析

首先,这个类有以下成员变量

构造函数

getFileCount()方法

实现Runnable接口重写其中的run()方法

getFolderName()方法

FolderFileCount类解析

第一部分

 第二部分——两种对文件的排序方法

第三部分——创建任务列表

 第四部分——创建线程池和执行任务

第五部分——等待任务执行完成或超时并做出回应


了解线程池

什么是线程池?

        当编写程序时,如果我们的线程创建的过多,就容易引发内存溢出,因此我们就有必要使用线程池的技术了。

        在 Java 中,线程池是一种用于管理和复用线程的机制。使用线程池可以避免频繁创建和销毁线程带来的开销,提高程序的性能和资源利用率。

        

线程池的优势


总体来说,线程池有如下的优势:

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

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

(3)提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
 

线程池的参数

  1. corePoolSize(必需):核心线程数。默认情况下,核心线程会一直存活,但是当将 allowCoreThreadTimeout 设置为 true 时,核心线程也会超时回收。
  2. maximumPoolSize(必需):线程池所能容纳的最大线程数。当活跃线程数达到该数值后,后续的新任务将会阻塞。
  3. keepAliveTime(必需):线程闲置超时时长。如果超过该时长,非核心线程就会被回收。如果将 allowCoreThreadTimeout 设置为 true 时,核心线程也会超时回收。
  4. unit(必需):指定 keepAliveTime 参数的时间单位。常用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分)。
  5. workQueue(必需):任务队列。通过线程池的 execute() 方法提交的 Runnable 对象将存储在该参数中。其采用阻塞队列实现。
  6. threadFactory(可选):线程工厂。用于指定为线程池创建新线程的方式。
  7. handler(可选):拒绝策略。当达到最大线程数时需要执行的饱和策略。

工作原理图

流程描述

        线程池的运作大致流程如下。

  • CORE_POOL_SIZE:核心线程数为 5,表示线程池中最少保持的线程数量。
  • MAX_POOL_SIZE:最大线程数为 10,表示线程池中可同时运行的最大线程数量。
  • QUEUE_CAPACITY:任务队列容量为 100,表示能够存放等待执行任务的队列的最大长度。
  • KEEP_ALIVE_TIME:线程的空闲时间为 1 秒。如果线程池中的线程数量超过核心线程数,并且在此空闲时间内没有新任务到达,则这些多余的线程将被终止。
  1.  在这些参数下,假设有这样一个场景——此时有200个任务提交给了线程池要进行执行。
  2.  由于设定了核心线程数为5,所以线程池中的前五个任务会立即启动然后执行,因为他们的个数没有超过核心线程数。
  3.  然后剩下的195个任务会尝试放在任务队列中等待执行,但是因为已经设定了任务队列的最大容量为100,所以此时应该开辟新的线程。在该场景下,根据最大线程数10,可以得出开辟5个新的线程来执行第6,7,8,9,10个任务
  4.  之后如果线程池中的线程数量在一段时间内没有新任务到达,并且此时存在的线程超过了核心线程数,那么多余的线程会被终止。在模拟场景中,这些多余的线程在空闲 1 秒后会被终止并从线程池中移除。
  5.  如果活动线程数已经达到最大线程数,并且任务队列也一直已满,那么会根据设置的饱和策略来处理新的任务。在本例中采用的是CallerRunsPolicy策略,即由提交任务的线程来执行该任务。

        

        假设有一个线程池核心线程数为5,最大线程数为10,队列容量为200,当线程池的核心线程数已经满员(5个线程正在工作),且任务队列已满(有200个任务在阻塞队列上等待执行),这时再有新的任务提交到线程池时,线程池会根据拒绝策略来处理这些任务。因为最大线程数为10,所以在线程池中的线程数量达到10个时,新提交的任务将会被拒绝。故此时一般情况下有第211个任务的时候,就会采用拒绝策略了。

线程池应用——实现读取指定路径下的前多少个文件夹下文件的数量

先把完整的代码贴到这里

package com.qcby.demoforspringboot.test;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

class FileCountTask implements Runnable {
    private final String folderPath;
    private final String folderName;
    private int fileCount;

    public FileCountTask(String folderPath, String folderName) {
        this.folderPath = folderPath;
        this.folderName = folderName;
        this.fileCount = 0;
    }

    public int getFileCount() {
        return fileCount;
    }

    @Override
    public void run() {
        countFiles(new File(folderPath));
    }

    private void countFiles(File folder) {
        if (folder.isFile()) {
            fileCount++;
        } else if (folder.isDirectory()) {
            File[] fileList = folder.listFiles();
            if (fileList != null) {
                for (File file : fileList) {
                    countFiles(file);
                }
            }
        }
    }

    public String getFolderName() {
        return folderName;
    }
}

public class FolderFileCount {
    public static void main(String[] args) {
        // 根文件夹路径
        File rootFolder = new File("D:\\");

        // 获取根文件夹下的子文件夹
        File[] subFolders = rootFolder.listFiles(File::isDirectory);
        if (subFolders == null || subFolders.length < 5) {  // 如果子文件夹数量小于5,则输出提示信息并退出程序
            System.out.println("There are not enough subfolders in the root folder.");
            return;
        }

/*        // 按最后修改时间对子文件夹进行排序
        Arrays.sort(subFolders, Comparator.comparingLong(File::lastModified).reversed());*/
        Arrays.sort(subFolders, Comparator.comparing(File::getName));

        // 创建任务列表
        List<FileCountTask> tasks = new ArrayList<>();
        for (int i = 0; i < 5; i++) {  // 只处理前5个子文件夹
            File subFolder = subFolders[i];
            FileCountTask task = new FileCountTask(subFolder.getPath(), subFolder.getName());
            tasks.add(task);
        }

        // 创建线程池,并执行任务
        ExecutorService executorService = Executors.newFixedThreadPool(3);  // 创建固定线程数为3的线程池
        for (FileCountTask task : tasks) {
            executorService.execute(task);  // 提交任务给线程池执行
        }
        executorService.shutdown();  // 关闭线程池,不再接受新的任务

        try {
            // 等待任务执行完成或超时
            boolean finished = executorService.awaitTermination(1, TimeUnit.MINUTES);  // 最多等待1分钟
            if (finished) {  // 如果任务执行完成
                // 输出文件夹及对应的文件数量
                for (int i = 0; i < tasks.size(); i++) {
                    FileCountTask task = tasks.get(i);
                    System.out.println("Folder " + task.getFolderName() + ": " + task.getFileCount() + " files");
                }
            } else {  // 如果任务执行超时
                System.out.println("Task execution timeout!");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

        

任务类解析

首先,这个类有以下成员变量

  • folderPath:文件夹路径,表示要进行文件数量统计的文件夹的路径。
  • folderName:文件夹名称,表示要进行文件数量统计的文件夹的名称。
  • fileCount:文件数量,表示统计得到的文件的总数。

构造函数

  • FileCountTask(String folderPath, String folderName):构造函数,用于初始化文件夹路径和文件夹名称,并将文件数量初始化为0。
public FileCountTask(String folderPath, String folderName) {
    this.folderPath = folderPath;
    this.folderName = folderName;
    this.fileCount = 0;
}

getFileCount()方法

        获取文件数量,返回文件数量的值。

public int getFileCount() {
    return fileCount;
}

实现Runnable接口重写其中的run()方法

        实现Runnable接口的run()方法,用于启动文件数量统计任务。在这个方法中,会调用countFiles()方法来进行文件数量统计。

private void countFiles(File folder) {
    if (folder.isFile()) {
        fileCount++;
    } else if (folder.isDirectory()) {
        File[] fileList = folder.listFiles();
        if (fileList != null) {
            for (File file : fileList) {
                countFiles(file);
            }
        }
    }
}

getFolderName()方法

获取文件夹名称,返回文件夹名称的值。

public String getFolderName() {
    return folderName;
}

FolderFileCount类解析

        这个FolderFileCount类用于统计指定根文件夹下前5个子文件夹的文件数量。

第一部分

  1. 创建了一个File对象rootFolder,表示根文件夹的路径。
  2. 使用listFiles方法获取根文件夹下的所有子文件夹,存储在File数组subFolders中。
  3. 然后判断subFolders是否为空或子文件夹数量是否小于5,如果是,则输出提示信息并退出程序。
// 根文件夹路径
File rootFolder = new File("D:\\");

// 获取根文件夹下的子文件夹
File[] subFolders = rootFolder.listFiles(File::isDirectory);
if (subFolders == null || subFolders.length < 5) {  // 如果子文件夹数量小于5,则输出提示信息并退出程序
    System.out.println("There are not enough subfolders in the root folder.");
    return;
}

 第二部分——两种对文件的排序方法

  1. 使用Arrays.sort方法对subFolders数组进行排序,排序的依据是子文件夹的名称(按字母顺序)这里也注释掉的是排序依据最后修改时间的代码。
/*      // 按最后修改时间对子文件夹进行排序
        Arrays.sort(subFolders, Comparator.comparingLong(File::lastModified).reversed());*/
        Arrays.sort(subFolders, Comparator.comparing(File::getName));

第三部分——创建任务列表

  1. 创建了一个空的任务列表tasks,用于存储FileCountTask任务对象。
  2. 使用一个循环,只处理前5个子文件夹:
    • 获取每个子文件夹的路径和名称。
    • 创建一个FileCountTask任务对象,并将其加入到tasks列表中。

List<FileCountTask> tasks = new ArrayList<>();
for (int i = 0; i < 5; i++) {  // 只处理前5个子文件夹
    File subFolder = subFolders[i];
    FileCountTask task = new FileCountTask(subFolder.getPath(), subFolder.getName());
    tasks.add(task);
}

 第四部分——创建线程池和执行任务

  1. 使用Executors.newFixedThreadPool方法创建了一个固定线程数为5的线程池executorService
  2. 使用一个循环,遍历tasks列表中的每个任务对象:
    • 调用executorService.execute方法提交任务给线程池执行。
  3. 调用executorService.shutdown方法,关闭线程池,不再接受新的任务。
// 创建线程池,并执行任务
ExecutorService executorService = Executors.newFixedThreadPool(5);  // 创建固定线程数为3的线程池
for (FileCountTask task : tasks) {
    executorService.execute(task);  // 提交任务给线程池执行
}
executorService.shutdown();  // 关闭线程池,不再接受新的任务

第五部分——等待任务执行完成或超时并做出回应

  1. 使用executorService.awaitTermination方法等待任务执行完成或超时。
    • 这里设置最多等待1分钟。
  2. 如果任务执行完成,则输出每个文件夹及对应的文件数量:
    • 使用一个循环,遍历tasks列表中的每个任务对象。
    • 调用task.getFolderName方法获取文件夹名称。
    • 调用task.getFileCount方法获取文件数量。
    • 输出文件夹名称和文件数量的信息。
  3. 如果任务执行超时,则输出提示信息。
        try {
            // 等待任务执行完成或超时
            boolean finished = executorService.awaitTermination(1, TimeUnit.MINUTES);  // 最多等待1分钟
            if (finished) {  // 如果任务执行完成
                // 输出文件夹及对应的文件数量
                for (int i = 0; i < tasks.size(); i++) {
                    FileCountTask task = tasks.get(i);
                    System.out.println("Folder " + task.getFolderName() + ": " + task.getFileCount() + " files");
                }
            } else {  // 如果任务执行超时
                System.out.println("Task execution timeout!");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值