每日三个JAVA经典面试题(十七)

1.Java 中的线程池是如何实现的

Java中的线程池主要通过java.util.concurrent包提供的Executor框架实现。线程池的核心是重用一组现有线程来执行任务,而不是为每个任务创建新线程。这样做可以减少因频繁创建和销毁线程带来的开销,提高系统资源的利用率,并提供更好的系统性能。在Java中,线程池的实现主要依赖于以下几个关键类:

1. Executor接口

Executor是最基础的接口,它提供了一个execute(Runnable command)方法,用于执行任务。然而,这个接口本身并不直接提供线程池功能。

2. ExecutorService接口

ExecutorServiceExecutor的子接口,提供了更丰富的线程池管理功能,包括任务提交、线程池关闭等。它定义了一系列方法,允许对任务的提交进行更细致的控制,并能返回任务的执行结果。

3. ThreadPoolExecutor类

ThreadPoolExecutorExecutorService接口的一个实现类,提供了线程池的实现。它允许配置线程池的核心参数,如核心线程数、最大线程数、线程存活时间、任务队列等。ThreadPoolExecutor是实现线程池的直接方式。

4. ScheduledExecutorService接口

ScheduledExecutorServiceExecutorService的子接口,提供了任务的定时执行和周期性执行功能。

5. ScheduledThreadPoolExecutor类

ScheduledThreadPoolExecutorScheduledExecutorService接口的实现类,支持任务的定时执行和周期性执行。

实现原理

  • 任务队列:线程池使用一个阻塞队列来存储待执行的任务。当所有线程都忙时,新提交的任务会被放入队列中等待。
  • 线程管理:线程池根据配置的参数(如核心线程数、最大线程数)来创建和管理线程。当任务提交给线程池时,如果核心线程都在忙且任务队列未满,则任务会被加入队列。如果核心线程都在忙且任务队列已满,线程池会创建新的线程来处理任务,直到线程数达到最大线程数限制。
  • 线程回收:当线程空闲时间超过配置的存活时间时,线程池可能会回收线程以节省资源。

示例

创建固定大小的线程池并提交任务:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadPoolDemo {public static void main(String[] args) {// 创建一个固定大小的线程池ExecutorService executor = Executors.newFixedThreadPool(4);// 提交任务给线程池执行for (int i = 0; i < 10; i++) {int taskId = i;executor.execute(() -> {System.out.println("Executing task " + taskId + " inside : " + Thread.currentThread().getName());});}// 关闭线程池executor.shutdown();}
}

在这个示例中,通过Executors.newFixedThreadPool(4)创建了一个固定大小为4的线程池,然后提交了10个任务。线程池会重用这4个线程来执行所有提交的任务。

2.创建线程池的几个核心构造参数

在Java中,通过ThreadPoolExecutor类创建线程池时,需要指定几个核心构造参数,这些参数对线程池的行为和性能有重要影响。以下是这些核心构造参数:

1. corePoolSize

  • 类型int
  • 描述:线程池的基本大小,即在没有任务执行时线程池的大小,并且只有在工作队列满了之后才会创建超过这个数量的线程。

2. maximumPoolSize

  • 类型int
  • 描述:线程池允许的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。

3. keepAliveTime

  • 类型long
  • 描述:当线程数大于核心线程数时,这是多余空闲线程在终止前等待新任务的最长时间。

4. unit

  • 类型TimeUnit
  • 描述keepAliveTime参数的时间单位。TimeUnit是一个枚举,提供了如TimeUnit.SECONDSTimeUnit.MILLISECONDS等时间单位。

5. workQueue

  • 类型BlockingQueue<Runnable>
  • 描述:用来存储等待执行的任务的阻塞队列。可以选择不同类型的队列,如LinkedBlockingQueueArrayBlockingQueue等,不同类型的队列在处理不同类型的任务时效率不同。

6. threadFactory(可选)

  • 类型ThreadFactory
  • 描述:用于设置创建线程的工厂。可以通过实现ThreadFactory接口自定义线程创建方式(如设置线程名、优先级等)。

7. handler(可选)

  • 类型RejectedExecutionHandler
  • 描述:当任务无法被线程池执行时(例如,队列已满且线程数已达到最大值),拒绝任务的处理程序。常见的有四种策略:ThreadPoolExecutor.AbortPolicy(抛出异常)、ThreadPoolExecutor.CallerRunsPolicy(调用者运行任务)、ThreadPoolExecutor.DiscardPolicy(忽略任务)、ThreadPoolExecutor.DiscardOldestPolicy(丢弃队列最前面的任务,然后重试执行任务)。

示例

import java.util.concurrent.*;public class ThreadPoolExample {public static void main(String[] args) {int corePoolSize = 5;int maximumPoolSize = 10;long keepAliveTime = 5000;TimeUnit unit = TimeUnit.MILLISECONDS;BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(100);ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());executor.execute(() -> {System.out.println("Task is running.");});executor.shutdown();}
}

在这个示例中,创建了一个ThreadPoolExecutor实例,并指定了核心构造参数,然后提交了一个简单的任务来执行。

3.线程池中的线程是怎么创建的?是一开始就随着线程池的启动创建好的吗?线程池中线程的创建方式主要取决于线程池的配置参数和实际运行时的任务需求。线程池中线程的创建和管理遵循以下基本规则:

初始化核心线程

  • 立即创建:在某些线程池配置中,可以选择在线程池创建时立即初始化并启动核心线程数(corePoolSize),即使没有任务提交。这可以通过调用ThreadPoolExecutorprestartCoreThread()prestartAllCoreThreads()方法实现。
  • 按需创建:默认情况下,核心线程是按需创建的,即只有在任务被提交并且当前运行的线程数小于corePoolSize时,才会创建和启动新的线程。

超过核心线程数的线程创建

  • 当所有核心线程都在忙碌,并且内部任务队列已满时,如果当前线程总数小于最大线程数(maximumPoolSize),线程池会创建新的线程来处理任务。
  • 这些超出核心线程数的线程在空闲时会存在一段时间(由keepAliveTime参数控制),如果在这段时间内没有新的任务分配给它们,它们将被回收。

线程创建的实际流程

  1. 任务提交:当一个任务被提交到线程池时,线程池会进行如下判断:
    • 如果当前运行的线程数小于corePoolSize,则创建并启动一个新的线程来执行提交的任务(即使其他工作线程空闲)。
    • 如果当前运行的线程数等于或大于corePoolSize,但任务队列未满,任务会被加入队列等待执行。
    • 如果任务队列已满,并且当前运行的线程数小于maximumPoolSize,线程池会尝试创建新的线程来直接执行任务。
    • 如果当前运行的线程数达到maximumPoolSize,新提交的任务会被拒绝执行处理器处理。

通过这种机制,线程池可以有效地管理线程的创建和销毁,以适应不同的工作负载,避免在高负载下创建过多的线程导致资源耗尽,同时也避免在低负载时保持过多的空闲线程导致资源浪费。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/756131.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android逆向(二)-系统调试开关

Android逆向(二)-系统调试开关 本篇文章主要介绍下android下的系统调试开关. 1: build.prop简介 android中有一些常用的配置信息都存放在一个文件中,如:设备系统/版本号/Cpu等信息. 而这个文件就是/system/build.prop 我们先简单看下这个文件: zhzh:~/workSpace$ adb she…

【项目实践day06】JWT令牌相关

什么是JWT 简洁的、自包含的格式&#xff0c;用于在通信双方以json数据格式安全的传输信息。 由于数字签名的存在&#xff0c;这些信息是可靠的。 jwt就是将原始的json数据格式进行了安全的封装&#xff0c;这样就可以直接基于jwt在通信双方安全的进行信息传输了。简洁&#…

Playwright中locator() 方法快速定位网页元素[全面总结]

Playwright 是一个用于浏览器自动化的库&#xff0c;它支持多种浏览器和多种语言。在 Playwright 中&#xff0c;page.locator() 方法用于创建一个元素定位器&#xff08;Element Locator&#xff09;。元素定位器是一个强大的工具&#xff0c;可以帮助你在页面上找到并操作元素…

【前端基础】什么是视口?

视口 了解视口相关概念及理想视口的设置 是移动Web开发非常重要环节。 什么是视口&#xff1f; 视口简单来说就是浏览器显示页面内容的区域。 在PC端&#xff0c;正常的视口宽度就是整个浏览器的窗口可视区的宽度&#xff0c;会随着浏览器窗口大小的重置而缩放&#xff1b;…

CTF 题型 SSRF攻击例题总结

CTF 题型 SSRF攻击&例题总结 文章目录 CTF 题型 SSRF攻击&例题总结Server-side Request Forgery 服务端请求伪造SSRF的利用面1 任意文件读取 前提是知道要读取的文件名2 探测内网资源3 使用gopher协议扩展攻击面Gopher协议 &#xff08;注意是70端口&#xff09;python…

前端项目,个人笔记(五)【图片懒加载 + 路由配置 + 面包屑 + 路由行为修改】

目录 1、图片懒加载 步骤一&#xff1a;自定义全局指令 步骤二&#xff1a;代码中使用 ​编辑步骤三&#xff1a;效果查看 步骤四&#xff1a;代码优化 2、封装组件案例-传对象 3、路由配置——tab标签 4、根据tab标签添加面包屑 4.1、实现 4.2、bug&#xff1a;需要…

智能合约 之 部署ERC-20

Remix介绍 Remix是一个由以太坊社区开发的在线集成开发环境&#xff08;IDE&#xff09;&#xff0c;旨在帮助开发者编写、测试和部署以太坊智能合约。它提供了一个简单易用的界面&#xff0c;使得开发者可以在浏览器中直接进行智能合约的开发&#xff0c;而无需安装任何额外的…

进销存管理完整方案:那些让人头疼的进销存难题及解决方法!

什么是进销存管理&#xff1f;为何进销存管理在企业管理中如此重要&#xff1f;进销存管理的核心模块包括哪些&#xff1f;为何企业在进销存管理中常常遭遇前后方协作不畅、数据不同步等痛点&#xff1f;又该如何针对进销存管理痛点进行优化&#xff1f;本文将从进销存管理的基…

代码随想录训练营第50天 | LeetCode 123.买卖股票的最佳时机III、LeetCode 188.买卖股票的最佳时机IV

LeetCode 123.买卖股票的最佳时机III 文章讲解&#xff1a;代码随想录(programmercarl.com) 视频讲解&#xff1a;动态规划&#xff0c;股票至多买卖两次&#xff0c;怎么求&#xff1f; | LeetCode&#xff1a;123.买卖股票最佳时机III_哔哩哔哩_bilibili 思路 代码如下&am…

java实现kml文件下载接口

根据业务需求&#xff0c;需提供一个下载kml格式航线文件的HTTP GET接口 示例代码 package com.kyrielx.kmzdemo.controller;import org.apache.commons.io.FileUtils; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org…

git是什么git能做什么

git是什么 git 是一个免费、开源、分布式的版本控制系统&#xff0c;跟踪个人和团队在项目上协作时的变更历史。随着开发者对项目进行更改&#xff0c;可以随时恢复项目的任何早期版本。 git能做什么 开发者可以通过git查看项目历史&#xff0c;了解&#xff1a; - 做了哪些…

5.首页搜索与瀑布流

搜索栏 官网 https://vkuviewdoc.fsq.pub/components/search.html 是否开启右边控件 该控件为类似按钮形式&#xff0c;可以设置为"搜索"或者"取消"等内容&#xff0c;如果开启动画效果&#xff0c;用户确认搜索后&#xff0c;该控件会自动消失 show-ac…

各种窗函数对脉压结果的影响

雷达导论专栏总目录链接: 雷达导论专栏总目录-CSDN博客 1. 各类窗函数 有几个窗函数的系数可配,配置如下: tukeywin(N,0.75)kaiser(N,2.5)gausswin(N,1.5)taylorwin(N,3,-24)2. 时域加窗 时域加窗时,直接加在匹配滤波函数上:Htw=exp(1j*pi*K*tp.^2).*win;。那么矩形窗就相…

使用jQuery的autocomplete实现数据查询一次,联想自动补全

书接上回&#xff0c;上次说到在jsp页面中&#xff0c;通过监听输入框的数值变化&#xff0c;实时查询数据库&#xff0c;得到返回值使用autocomplete属性自动补全&#xff0c;实现一个联想补全辅助操作&#xff0c;链接&#xff1a;使用jquery的autocomplete属性实现联想补全操…

73_Pandas获取分位数/百分位数

73_Pandas获取分位数/百分位数 使用 quantile() 方法获取 pandas 中 DataFrame 或 Series 的分位数/百分位数。 目录 Quantile() 的基本用法指定要获取的分位数/百分位数&#xff1a;参数 q指定interpolation方法&#xff1a;参数interpolation 数据类型 dtype 的差异 指定行…

二、Kubernetes(k8s)中部署项目wordpress(php博客项目,数据库mysql)

前期准备 1、关机顺序 2、开机顺序 (1)、k8s-ha1、k8s-ha2 (2)、master01、master02、master03 (3)、node01、node02 一、集群服务对外提供访问&#xff0c;需要通过Ingress代理发布域名 mast01上传 ingress-nginx.yaml node01、node02 上传 ingress-nginx.tar 、kube-webh…

鸿蒙开发实战:【网络管理-Socket连接】

介绍 本示例主要演示了Socket在网络通信方面的应用&#xff0c;展示了Socket在两端设备的连接验证、聊天通信方面的应用。 效果预览 使用说明 1.打开应用&#xff0c;点击用户文本框选择要登录的用户&#xff0c;并输入另一个设备的IP地址&#xff0c;点击确定按钮进入已登录…

【C++】用红黑树模拟实现set、map

目录 前言及准备&#xff1a;一、红黑树接口1.1 begin1.2 end1.3 查找1.4 插入1.5 左单旋和右单旋 二、树形迭代器&#xff08;正向&#xff09;2.1 前置 三、模拟实现set四、模拟实现map 前言及准备&#xff1a; set、map的底层结构是红黑树&#xff0c;它们的函数通过调用红…

鸿蒙Harmony应用开发—ArkTS声明式开发(基础手势:Toggle)

组件提供勾选框样式、状态按钮样式及开关样式。 说明&#xff1a; 该组件从API Version 8开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 仅当ToggleType为Button时可包含子组件。 接口 Toggle(options: { type: ToggleType, is…

一台电脑安装多个版本node,如何切换使用

直接上答案&#xff0c;请安装nvm——nodejs的版本管理工具 官网地址在此&#xff1a;nvm文档手册 - nvm是一个nodejs版本管理工具 - nvm中文网 (uihtm.com) 1.由于我电脑本来就有node14&#xff0c;所以需要先卸载 原来的node&#xff0c;建议在软件目录自带的node文件夹中点…