当前位置: 首页 > news >正文

Java 之多线程高级

本文将深入探讨Java线程池的方方面面,从线程状态介绍开始,逐步深入线程池的原理、Executors默认线程池的使用,自定义线程池的创建,ThreadPoolExecutor的参数详解,以及非默认任务拒绝策略的应用。最后,我们将通过一个小案例来展示如何使用线程池来提高程序效率。

1. 线程状态介绍

Java线程的生命周期可以用以下状态来描述:

  • 新建 (New): 线程对象被创建但还未启动。

  • 可运行 (Runnable): 线程已经启动,正在等待获取 CPU 时间片执行。

  • 运行 (Running): 线程获得了 CPU 时间片,正在执行任务。

  • 阻塞 (Blocked): 线程正在等待某些事件发生,例如 I/O 操作完成、获取锁等。

  • 等待 (Waiting): 线程正在等待其他线程通知它才能继续执行。

  • 超时等待 (Timed Waiting): 线程正在等待其他线程通知它,但有一个超时时间限制。

  • 终止 (Terminated): 线程已经执行完毕,无法再次启动。

代码示例:

public class ThreadStateDemo {public static void main(String[] args) {// 创建一个新的线程Thread thread = new Thread(() -> {// 模拟线程运行逻辑System.out.println("线程正在运行...");try {// 模拟阻塞操作Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("线程运行结束...");});// 启动线程thread.start();// 获取线程状态并打印System.out.println("线程状态:" + thread.getState()); }
}

2. 线程池---基本原理

线程池是一种管理和复用线程的技术,它通过维护一组可重用线程来处理任务,避免频繁创建和销毁线程带来的性能开销。

基本原理:

  • 线程池维护一个工作线程集合: 用于执行提交的任务。

  • 任务队列: 用于存储等待执行的任务。

  • 任务提交: 当有新的任务提交时,线程池会判断是否有空闲线程,如果有则直接分配任务给该线程执行,否则将任务加入任务队列等待。

  • 线程回收: 当线程执行完任务后,不会立即销毁,而是进入等待状态,等待新的任务分配。

优势:

  • 减少创建和销毁线程的开销: 重用线程可以避免频繁创建和销毁线程带来的性能损耗。

  • 控制并发线程数量: 可以有效地控制并发线程数,避免资源耗尽。

  • 提高响应速度: 当任务提交时,如果线程池中有空闲线程,可以立即执行任务,提高响应速度。

  • 简化线程管理: 通过线程池可以方便地管理线程,无需手动创建和销毁线程。

3. 线程池---Executors默认线程池

java.util.concurrent.Executors 类提供了一些常用的线程池创建方法,包括:

  • newCachedThreadPool(): 创建一个可缓存的线程池,如果线程池长度超过处理需要,可回收空闲线程,如果没有空闲线程则创建新的线程。

  • newFixedThreadPool(int nThreads): 创建一个固定线程数的线程池,如果提交的任务数超过线程数,则会将任务放入队列等待。

  • newSingleThreadExecutor(): 创建一个单线程化的线程池,保证任务按照顺序执行。

  • newScheduledThreadPool(int corePoolSize): 创建一个可以执行延迟任务或周期性任务的线程池。

代码示例:

public class ExecutorsDemo {public static void main(String[] args) {// 创建一个缓存线程池ExecutorService cachedThreadPool = Executors.newCachedThreadPool();// 创建一个固定线程数的线程池ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);// 创建一个单线程化的线程池ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();// 创建一个可以执行延迟任务或周期性任务的线程池ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);}
}

4. 线程池---Executors创建指定上限的线程池

可以使用 Executors 类中的 newFixedThreadPool(int nThreads) 方法来创建指定上限的线程池。

代码示例:

public class FixedThreadPoolDemo {public static void main(String[] args) {// 创建一个固定线程数为 5 的线程池ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);// 提交任务到线程池for (int i = 0; i < 10; i++) {fixedThreadPool.execute(() -> {// 模拟任务执行逻辑System.out.println("线程池中的线程正在执行任务...");});}// 关闭线程池fixedThreadPool.shutdown();}
}

5. 线程池---ThreadPoolExecutor

java.util.concurrent.ThreadPoolExecutor 类是创建线程池的核心类,它提供了更加灵活的线程池配置方式。

代码示例:

public class ThreadPoolExecutorDemo {public static void main(String[] args) {// 创建一个线程池ThreadPoolExecutor executor = new ThreadPoolExecutor(5, // corePoolSize:核心线程数10, // maximumPoolSize:最大线程数60, // keepAliveTime:空闲线程存活时间TimeUnit.SECONDS, // unit:空闲线程存活时间单位new LinkedBlockingQueue<>(100), // workQueue:任务队列new ThreadPoolExecutor.AbortPolicy() // handler:任务拒绝策略);// 提交任务到线程池for (int i = 0; i < 10; i++) {executor.execute(() -> {// 模拟任务执行逻辑System.out.println("线程池中的线程正在执行任务...");});}// 关闭线程池executor.shutdown();}
}

6. 线程池---参数详解(创建线程池对象的参数)

  • corePoolSize: 核心线程数,即使线程池处于空闲状态,也始终保持核心线程数数量的线程存活。

  • maximumPoolSize: 最大线程数,当任务队列已满且线程池中线程数量小于最大线程数时,会创建新的线程来处理任务,直到线程数量达到最大线程数。

  • keepAliveTime: 空闲线程存活时间,当线程池中线程数量超过核心线程数时,如果空闲线程在 keepAliveTime 时间内没有新的任务提交,则会回收该线程。

  • unit: 空闲线程存活时间单位。

  • workQueue: 任务队列,用于存放等待执行的任务。

  • threadFactory:  创建线程工厂,不能为null

  • handler: 任务拒绝策略,当任务队列已满且线程池中线程数量达到最大线程数时,会触发任务拒绝策略。

详解如下:

                1.corePoolSize:核心线程的最大值,不能小于0
                2.maximumPoolSize:最大线程数,不能小于等于0,maximumPoolSize >= corePoolSize
                3.keepAliveTime:  空闲线程最大存活时间,不能小于0
                4.unit:时间单位
                5.workQueue:任务队列,不能为null
                6.threadFactory:创建线程工厂,不能为null      
                7.handler:任务的拒绝策略,不能为null 

代码示例:

public class ThreadPoolExecutorDemo {public static void main(String[] args) {// 创建一个线程池ThreadPoolExecutor executor = new ThreadPoolExecutor(5, // corePoolSize:核心线程数10, // maximumPoolSize:最大线程数60, // keepAliveTime:空闲线程存活时间TimeUnit.SECONDS, // unit:空闲线程存活时间单位new LinkedBlockingQueue<>(100), // workQueue:任务队列new ThreadPoolExecutor.AbortPolicy() // handler:任务拒绝策略);// 提交任务到线程池for (int i = 0; i < 10; i++) {executor.execute(() -> {// 模拟任务执行逻辑System.out.println("线程池中的线程正在执行任务...");});}// 关闭线程池executor.shutdown();}
}

7. 线程池---非默认任务拒绝策略

当任务队列已满且线程池中线程数量达到最大线程数时,会触发任务拒绝策略。ThreadPoolExecutor 类提供了一些默认的任务拒绝策略,也可以自定义任务拒绝策略。

  • AbortPolicy: 抛出 RejectedExecutionException 异常,这是默认的拒绝策略。

  • CallerRunsPolicy: 由提交任务的线程执行该任务,可以防止线程池被阻塞。

  • DiscardPolicy: 直接丢弃任务,不做任何处理。

  • DiscardOldestPolicy: 丢弃队列中最老的任务,然后尝试重新提交当前任务。

代码示例:

// 使用 CallerRunsPolicy 作为拒绝策略
ThreadPoolExecutor executor = new ThreadPoolExecutor(5,10,60,TimeUnit.SECONDS,new LinkedBlockingQueue<>(100),new ThreadPoolExecutor.CallerRunsPolicy()
);// 使用自定义拒绝策略
class MyRejectionHandler implements RejectedExecutionHandler {@Overridepublic void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {System.out.println("任务被拒绝,执行自定义处理逻辑...");}
}ThreadPoolExecutor executor = new ThreadPoolExecutor(5,10,60,TimeUnit.SECONDS,new LinkedBlockingQueue<>(100),new MyRejectionHandler()
);

小案例: 使用线程池下载多个文件

import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadPoolDownload {public static void main(String[] args) {// 创建一个固定线程数为 5 的线程池ExecutorService executor = Executors.newFixedThreadPool(5);// 要下载的文件 URLString[] fileUrls = {"https://www.example.com/file1.zip","https://www.example.com/file2.txt","https://www.example.com/file3.jpg","https://www.example.com/file4.pdf","https://www.example.com/file5.mp4"};// 提交下载任务到线程池for (String fileUrl : fileUrls) {executor.execute(() -> downloadFile(fileUrl));}// 关闭线程池executor.shutdown();}// 下载文件private static void downloadFile(String fileUrl) {try {URL url = new URL(fileUrl);ReadableByteChannel rbc = Channels.newChannel(url.openStream());FileOutputStream fos = new FileOutputStream(fileUrl.substring(fileUrl.lastIndexOf('/') + 1));fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);fos.close();rbc.close();System.out.println("下载成功:" + fileUrl);} catch (IOException e) {System.err.println("下载失败:" + fileUrl + " - " + e.getMessage());}}
}

总结:

本文详细介绍了 Java 线程池的基础知识,从线程状态、线程池的基本原理、Executors默认线程池、自定义线程池的创建、ThreadPoolExecutor的参数详解,以及非默认任务拒绝策略等方面进行了深入讲解。最后,通过一个小案例展示了如何使用线程池来提高程序效率。希望本文能够帮助各位看官更好地理解和运用 Java 线程池,从而提升代码效率和性能。感谢各位看官的观看,下期见,谢谢~


http://www.mrgr.cn/news/29800.html

相关文章:

  • JavaScript高级程序设计基础(四)
  • SpringBoot单体服务无感更新启动,动态检测端口号并动态更新
  • Redis8:商户查询缓存2
  • react的创建与书写
  • 1小时构建Vue3知识体系之vue的生命周期函数
  • 高效实现自动化运维的Python工具开发与集成
  • 计算机毕业设计 家电销售展示平台的设计与实现 Java实战项目 附源码+文档+视频讲解
  • 使用python 将world的题库导入某学习软件的模板
  • 2024年9月python二级易错题和难题大全(附详细解析)(二)
  • MatchRFG:引领MemeCoin潮流,探索无限增长潜力
  • 论文不会写?分享6款AI论文写作免费一键生成网站!
  • Python urllib
  • LinuxC++的UDP服务器和客户端通信
  • 钢索缺陷检测系统源码分享
  • 1×1卷积核【super star 卷积核】
  • 人工智能与机器学习原理精解【21】
  • 图文检索(2):Visual-Linguistic Dependency Encoding for Image-Text Retrieval
  • 计算机网络的性能指标
  • 入门sentinel
  • 利用ClasserLoader来实现jar包加载并调用里面的方法
  • 英飞凌PSoC4000T示例工程
  • 洛谷 P1541 [NOIP2010 提高组] 乌龟棋
  • 机器学习实战—天猫用户重复购买预测
  • 【鸿蒙 HarmonyOS NEXT】组件嵌套滚动:nestedScroll
  • 【重学 MySQL】三十四、加密与解密函数
  • Linux进阶 修改文件所在组