首页 > 编程语言 >Java并发编程之newFixedThreadPool线程池

Java并发编程之newFixedThreadPool线程池

时间:2024-05-24 10:30:21浏览次数:14  
标签:execute Task Java newFixedThreadPool 任务 线程 executorService

随着计算机硬件性能的不断提升,多核CPU的普及,现代计算机系统的性能越来越强大。在这样的环境下,如何更好地利用计算机系统的性能优势,提高程序的运行效率,是每一个Java开发者需要思考的问题。

Java中提供了多线程编程的支持,但是在多线程编程中,线程的创建、启动、调度等都需要耗费一定的系统资源,如果线程的数量过多,会导致系统性能下降,甚至可能导致系统崩溃。为了解决这个问题,Java提供了线程池的概念,通过线程池可以对线程的数量进行管理,减少线程的创建和销毁次数,提高程序的效率和性能。

本文将介绍Java中的一个常用线程池类——newFixedThreadPool。

一、newFixedThreadPool的概述

newFixedThreadPool是Java中的一个线程池类,它是一个固定大小的线程池,线程的数量在创建线程池时就已经确定。线程池中的线程数量一旦被确定,就不会发生改变。在Java中,newFixedThreadPool()方法创建的线程池是一个固定大小的线程池,线程池中的线程数量是固定的,由构造函数传入的参数指定,而任务队列的大小则由内部的阻塞队列来决定。

在使用newFixedThreadPool()方法创建线程池时,它使用的是LinkedBlockingQueue,这是一个无界的阻塞队列,它的大小是没有限制的。因此,当任务提交到线程池时,如果线程池中的线程正在执行任务,那么新提交的任务将被放入LinkedBlockingQueue中等待执行,直到有可用的线程来执行任务。当队列已经满了时,新提交的任务将会被阻塞,直到有空闲线程来处理队列中的任务。

因此,使用newFixedThreadPool()方法创建线程池时,队列的大小实际上是无限制的,但是需要注意的是,如果任务提交速度过快,队列可能会无限制地增长,导致内存溢出等问题。因此,在实际使用中需要根据具体的场景来合理设置线程池的大小和任务队列的容量,以充分利用系统资源并保证系统的稳定性。

二、newFixedThreadPool的使用

使用newFixedThreadPool非常简单,只需要创建一个ThreadPoolExecutor对象,并向其提交任务即可。线程池会自动分配线程来执行任务,如果线程池中的线程数量不足,任务会被放入阻塞队列中等待执行。

下面是一个使用newFixedThreadPool的示例:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
 
public class Main {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 10; i++) {
            executorService.execute(new Task(i));
        }
        executorService.shutdown();
    }
}
 
class Task implements Runnable {
    private int id;
 
    public Task(int id) {
        this.id = id;
    }
 
    public void run() {
        System.out.println("Task " + id + " is running...");
    }
}

在这个例子中,创建了一个大小为5的线程池,并向其中提交了10个任务。每个任务只是简单地输出一条信息。任务的执行结果如下所示:

Task 0 is running...
Task 1 is running...
Task 2 is running...
Task 3 is running...
Task 4 is running...
Task 5 is running...
Task 6 is running...
Task 7 is running...
Task 8 is running...
Task 9 is running...

可以看到,线程池中的5个线程依次执行了任务,并输出了相关信息。

在提交任务时,需要注意线程池的关闭。在本例中,调用了executorService.shutdown()方法,该方法会向线程池中的所有线程发送一个中断信号,线程池中的所有线程都将退出。

三、newFixedThreadPool的优点

    简单易用:newFixedThreadPool是Java内置的线程池,使用起来非常简单,不需要手动创建线程、管理线程、调度线程等。
    稳定性好:newFixedThreadPool是一个固定大小的线程池,线程数量不会发生变化,因此稳定性较好,不容易因线程数量过多导致系统崩溃。
    高效性:newFixedThreadPool在任务队列中维护了一个阻塞队列,可以很好地处理大量的并发请求,提高了程序的效率和性能。
    良好的可扩展性:newFixedThreadPool可以通过修改线程池中的线程数量来适应不同的系统负载,提高了系统的可扩展性。

四、newFixedThreadPool的缺点

    线程数固定:newFixedThreadPool创建时需要指定线程数量,线程数量不能动态调整,因此无法适应不同的系统负载。
    队列长度有限:newFixedThreadPool使用阻塞队列存储任务,队列长度有限,如果队列已满,新的任务将无法提交,可能会导致任务丢失。
    任务执行时间不可控:newFixedThreadPool无法控制任务执行的时间,如果任务执行时间过长,会导致其他任务等待的时间过长,降低程序的效率和性能。

五、线程池的两种主要任务提交方法

在Java中,线程池的两种主要任务提交方法是execute()和submit()。它们虽然都是将任务提交到线程池中,但是在使用上有一些区别。

    返回值类型不同

execute()方法没有返回值,而submit()方法会返回一个Future对象,可以通过这个对象获取任务的执行结果。

    异常处理不同

execute()方法中如果任务执行过程中发生了异常,则异常会被传递到任务提交的地方,并由任务提交的线程来处理。而submit()方法中,如果任务执行过程中发生了异常,异常将被封装在Future对象中,直到调用Future.get()方法时才会将异常抛出。

    任务参数不同

execute()方法只能接受Runnable类型的任务,而submit()方法既可以接受Runnable类型的任务,也可以接受Callable类型的任务。

    提交方式不同

execute()方法是一种异步提交方式,即提交任务后立即返回,不会等待任务执行完成。而submit()方法是一种同步提交方式,即提交任务后会阻塞当前线程,直到任务执行完成。

    能否取消任务

submit()方法返回的Future对象可以用来取消任务,而execute()方法没有提供取消任务的方法。

总之,execute()方法比submit()方法更简单,适用于不需要处理返回值的情况,而submit()方法则更为灵活,可以处理返回值,并且支持取消任务等操作。

六、代码举例

使用 executorService.isTerminated() 可以判断线程池中的任务是否全部执行完毕,但需要注意的是,该方法只有在调用 executorService.shutdown() 方法后才会生效。因此,如果需要在所有任务执行完毕后调用 method06(),代码如下:

public class Main {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        //executorService.execute(() -> method01());
        //executorService.execute(() -> method02());
        //executorService.execute(() -> method03());
        //executorService.execute(() -> method04());
        //executorService.execute(() -> method05());
        executorService.execute(() -> {
            try {
                method01();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        executorService.execute(() -> {
            try {
                method02();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        executorService.execute(() -> {
            try {
                method03();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        executorService.execute(() -> {
            try {
                method04();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
        executorService.execute(() -> {
            try {
                method05();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
                executorService.shutdown();
        try {
            executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
            method06();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
 
    private static void method01() {
        // 解析1.txt文件
    }
 
    private static void method02() {
        // 解析2.txt文件
    }
 
    private static void method03() {
        // 解析3.txt文件
    }
 
    private static void method04() {
        // 解析4.txt文件
    }
 
    private static void method05() {
        // 解析5.txt文件
    }
    
    private static void method06() {
        // 在5个任务全部执行完毕后调用
    }
}

executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS) 是一个阻塞方法,会一直等待直到线程池中所有的任务都执行完毕或者等待超时,其作用是等待线程池中所有任务执行完成后,再继续执行主线程下面的代码。

具体来说,当调用 ExecutorService.shutdown() 方法后,线程池会停止接受新任务,但是还有一些任务正在执行或者在等待执行。当调用 awaitTermination() 方法时,它会一直阻塞,直到线程池中所有任务都执行完毕,或者等待超时,方法会返回 true 或者 false。如果返回 true,则表示线程池中的所有任务都已经执行完成;如果返回 false,则表示等待超时,线程池中还有未执行的任务。

在本题的情境下,我们使用 executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS) 方法可以让主线程一直阻塞等待所有任务完成,然后再继续执行下面的代码。

七、总结

newFixedThreadPool是Java中的一个常用线程池类,它可以有效地管理线程数量,提高程序的效率和性能。使用newFixedThreadPool可以避免线程数量过多导致系统性能下降和崩溃的问题,同时还可以提高系统的稳定性和可扩展性。

在实际使用过程中,需要根据具体的业务需求选择不同的线程池类,并合理设置线程池的参数,才能更好地发挥线程池的作用,提高程序的效率和性能。

标签:execute,Task,Java,newFixedThreadPool,任务,线程,executorService
From: https://www.cnblogs.com/shuilangyizu/p/18210108

相关文章

  • java Long 与long转换
    https://blog.51cto.com/u_16213451/7032984概述在Java中,Long是一个包装类,是long的封装类型。Long类提供了一些方法,用于在long和其他数据类型(如String)之间进行转换。本文将详细介绍如何在Java中实现Long和long之间的转换。流程以下是将Long和long相互转换的流程:步骤描述......
  • Java基于saas模式云MES制造执行系统源码Spring Boot + Hibernate Validation什么是MES
    Java基于saas模式云MES制造执行系统源码SpringBoot+HibernateValidation什么是MES系统?MES制造执行系统,通过互联网技术实现从订单下达到产品完成的整个生产过程进行优化管理。能有效地对生产现场的流程进行智能控制,防错防呆防漏,自动化集成各种制造信息,使管理者准确掌控工......
  • Java 9的模块化系统(JPMS):探讨Java 9引入的模块化系统,并解释其对Java生态的影响
    Java9模块化系统(JPMS)简介Java9模块系统,也被称为Java平台模块系统(JPMS),它是Java9的核心特性之一,用于改进Java的大型应用的封装性和可维护性。 JPMS的主要功能如下: 模块化代码:JPMS允许你将代码库划分为不同的模块,在没有显示声明的情况下,模块内部的类......
  • Java异常处理:共享在设计和实现Java异常处理策略时的最佳实践
    一、概览Java异常处理的最佳实践通常包括以下几个方面:有效使用Java提供的异常类型,创建和使用自定义异常,异常链,异常处理策略,以及记录和传播异常。二、有效使用Java提供的异常类型检查异常(CheckedException):这些异常是在编译阶段就会被检查的异常,通常是预期内的问题......
  • JavaScript 动态网页实例 —— 简单的表单验证
            表单验证是网页设计、特别是网站设计中必不可少的内容,狭义的验证指“用户是否进行了输入”、“输入的数据类型或格式是否符合要求”等,而广义的验证则包括口令确认与身份验证等内容。本章和下一章对表单验证的方式、方法进行介绍,本章只介绍一些简单的验证,包括:......
  • JAVA计算机毕业设计基于SpringBoot的疫苗接种管理系统(附源码+springboot+开题+论文)
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着全球范围内新冠疫情的爆发和持续,疫苗接种成为了防控疫情的重要手段。然而,疫苗接种的管理涉及到众多的环节和人员,如何有效地管理和跟踪接种者的接......
  • JAVA计算机毕业设计基于SpringBoot的窈窕之求食单平台的设计与实现(附源码+springboot+
    本系统(程序+源码)带文档lw万字以上 文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着生活节奏的加快和人们健康饮食意识的提升,越来越多的消费者开始关注菜谱的选择和制作。然而,市场上缺乏一个统一的、便捷的在线平台,用于提供丰富的......
  • 5 月,Java 岗位爆了???
    大家好,我是R哥。最近,R哥分享了两个特别有意思的面试辅导成功案例:35K*14薪入职了,这公司只要不裁员,我能一直呆下去。。干了2年多Java外包,终于脱离了!今天再分享一个振奋人心的面试辅导case:这个兄弟空窗期5个月,0Offer,面试一问项目就挂,报名面试辅导,1周内拿到3......
  • 递归,进程fork(),以及线程clone()之间的比较
    在计算机科学中,处理复杂任务的常见方法有递归、进程(通过fork创建),以及线程(通过clone创建)。这三种方式各有其独特的优势和适用场景。在本文中,我们将深入比较这三种方法,并展示它们在解决迷宫路径搜索问题时的不同实现方式,帮助开发者理解它们的异同,并根据不同的应用场景选择......
  • Java面向对象-常用类(String 、StringBuffer 、StringBuilder的使用与深入)
    常用类-字符串相关类1什么是StringString是不可变类,即一旦一个String对象被创建,包含在这个对象中的字符序列是不可改变的,直至该对象被销毁。String类是final类,不能有子类。2分类StringStringBufferStringBuilder3String的使用packagecom.qf.string_c......