-
前言1. Flowable中Java服务任务的依赖注入方法,比如使用Spring的@Autowired或构造函数注入,并确保服务任务类由Spring管理。2. 流程引擎配置中启用依赖注入和表达式解析的相关设置。3. 异步任务执行器的配置,确保其与Spring上下文集成,处理依赖注入的Bean。4. 常见问题排查,如检查Bean的作用域、是否正确使用delegateExpression等。5. 可能的示例代码或配置片段,帮助用户理解如何正确配置。另外,可能需要提醒用户注意流程定义文件中的服务任务配置是否正确,比如是否使用了expression或delegateExpression来引用Spring Bean,或者是否直接指定了Java类导致Spring的依赖注入不生效。最后,如果有必要,可以比较一下在非Spring环境下如何手动处理依赖注入,但大部分情况下用户可能在使用Spring,所以重点放在Spring集成方面。一、Java服务任务依赖注入场景:在实现JavaDelegate的类中注入Spring管理的Bean(如Service、Repository)。正确配置方法:步骤1:将服务类注册为Spring Bean@Component("myServiceTask") // 使用名称便于流程中引用public class MyServiceTask implements JavaDelegate { private final MyService myService; @Autowired // 构造器注入推荐 public MyServiceTask(MyService myService) { this.myService = myService; } @Override public void execute(DelegateExecution execution) { myService.businessMethod(); }}步骤2:在流程定义中引用Bean使用delegateExpression指向Spring Bean的名称:<serviceTask id="serviceTask1" name="My Task" flowable:delegateExpression="${myServiceTask}" />常见问题解决:Bean注入为null:确保类被Spring扫描(位于@ComponentScan路径下)。检查流程定义中是否错误使用class属性而非delegateExpression。确认流程引擎配置启用了Spring集成。表达式解析失败:在FlowableConfig中启用表达式解析和Spring注入:@Beanpublic SpringProcessEngineConfiguration processEngineConfiguration(DataSource dataSource, PlatformTransactionManager transactionManager) { SpringProcessEngineConfiguration config = new SpringProcessEngineConfiguration(); config.setDataSource(dataSource); config.setTransactionManager(transactionManager); config.setDatabaseSchemaUpdate(Flowable.DB_SCHEMA_UPDATE_TRUE); config.setAsyncExecutorEnabled(true); // 启用异步执行器 config.setExpressionManager(new SpringExpressionManager(applicationContext, null)); // 启用Spring表达式 return config;}二. 异步任务执行器配置当服务任务标记为异步(flowable:async="true")时,任务由异步执行器处理。配置异步执行器:@Beanpublic AsyncExecutor asyncExecutor(DataSource dataSource) { DefaultAsyncExecutor asyncExecutor = new DefaultAsyncExecutor(); asyncExecutor.setDataSource(dataSource); asyncExecutor.setCorePoolSize(5); // 核心线程数 asyncExecutor.setMaxPoolSize(10); // 最大线程数 asyncExecutor.setQueueSize(100); // 任务队列大小 return asyncExecutor;}在processEngineConfiguration中启用:config.setAsyncExecutor(asyncExecutor);config.setAsyncExecutorEnabled(true);异步任务依赖注入要点:Bean作用域:异步任务可能在新线程中运行,确保注入的Bean是线程安全的或使用@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)。事务管理:若异步任务涉及数据库操作,需配置@Transactional并确保事务传播正确。三、通过ApplicationContextAware接口的方式获取ApplicationContext对象实例可能的错误情况包括:没有在流程引擎配置中设置asyncExecutorEnabled为true,或者在服务任务中没有正确使用表达式导致注入失败。另外,Bean的作用域问题也可能导致依赖注入失败,例如,如果Bean的作用域是原型(prototype),但在注入时可能需要不同的处理方式。以下是我通过ApplicationContextAware接口的方式获取ApplicationContext对象实例,再通过applicationContext.getBean("myService")方法获取对应的bean代码示例:@Componentpublic class MyListener implements TaskListener, ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext arg0) throws BeansException { applicationContext = arg0; } @Override public void notify(DelegateTask delegateTask) { String processInsId = delegateTask.getProcessInstanceId(); MyService myService = (MyService) applicationContext.getBean("myService"); // TODO 执行service方法 System.out.println("==========执行监听器======"); }}四. 常见问题排查错误:无法解析表达式${myServiceTask}检查Bean名称是否匹配。确认流程引擎配置中设置了SpringExpressionManager。异步任务不执行检查asyncExecutor是否启动:调用asyncExecutor.start()。查看日志中是否有任务提交异常。事务不生效确保异步执行器配置了事务管理器:asyncExecutor.setTransactionManager(transactionManager);五. Spring Boot集成若使用flowable-spring-boot-starter,简化配置如下:(1) application.yml:flowable: async-executor-enabled: true database-schema-update: true async-executor: core-pool-size: 5 max-pool-size: 10 queue-size: 100(2) 服务任务类:@Componentpublic class EmailServiceTask implements JavaDelegate { private final EmailService emailService; public EmailServiceTask(EmailService emailService) { this.emailService = emailService; } @Override public void execute(DelegateExecution execution) { String recipient = (String) execution.getVariable("email"); emailService.send(recipient, "流程通知", "您的任务已处理完成。"); }}(3) 流程定义XML:<serviceTask id="sendEmail" flowable:delegateExpression="${emailServiceTask}" />六.总结依赖注入:确保服务任务类为Spring Bean,流程中使用delegateExpression引用。异步执行:配置AsyncExecutor并启用,注意线程安全和事务。ApplicationContextAware接口的方式获取ApplicationContext对象实例Spring集成:正确配置SpringProcessEngineConfiguration以支持表达式和Bean解析。转载自https://www.cnblogs.com/songweipeng/p/18820536
-
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba</artifactId> <version>2023.0.1.2</version> <relativePath/> </parent> <groupId>cn.cat</groupId> <artifactId>intelligent-teaching-backend</artifactId> <version>1.0-SNAPSHOT</version> <packaging>pom</packaging> <modules> <module>intelligent-teaching-commons</module> <module>intelligent-teaching-user</module> <module>intelligent-teaching-chat</module> <module>intelligent-teaching-exam</module> <module>intelligent-teaching-chat/intelligent-teaching-chat-api</module> <module>intelligent-teaching-chat/intelligent-teaching-chat-shared</module> <module>intelligent-teaching-exam/intelligent-teaching-exam-api</module> <module>intelligent-teaching-exam/intelligent-teaching-exam-shared</module> <module>intelligent-teaching-oss</module> <module>intelligent-teaching-job</module> <module>intelligent-teaching-search</module> <module>intelligent-teaching-ai</module> </modules> <properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring-ai.version>1.0.3</spring-ai.version> </properties> <repositories> <repository> <id>aliyunmaven</id> <url>https://maven.aliyun.com/repository/public</url> </repository> </repositories> <dependencies> <!-- https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-starter-webmvc-ui --> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all --> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <dependency> <groupId>cn.cat</groupId> <artifactId>intelligent-teaching-commons</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-generator --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>3.2.4</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2023.0.1.2</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>2023.0.1</version> <type>pom</type> <scope>import</scope> </dependency> <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.34</version> <scope>compile</scope> </dependency> <!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-spring-boot3-starter</artifactId> <version>3.5.9</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-annotation</artifactId> <version>3.5.9</version> </dependency> <!-- https://mvnrepository.com/artifact/com.alibaba/druid-spring-boot-starter --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-3-starter</artifactId> <version>1.2.23</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-starter-webmvc-ui --> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <version>2.6.0</version> </dependency> <!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all --> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.33</version> </dependency> <!-- https://mvnrepository.com/artifact/com.google.zxing/core --> <dependency> <groupId>com.google.zxing</groupId> <artifactId>core</artifactId> <version>3.5.3</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.17.0</version> </dependency> <!-- https://mvnrepository.com/artifact/io.minio/minio --> <dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>8.5.13</version> </dependency> <!-- https://mvnrepository.com/artifact/io.netty/netty-all --> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.115.Final</version> </dependency> <!-- https://mvnrepository.com/artifact/org.redisson/redisson-spring-boot-starter --> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter</artifactId> <version>3.43.0</version> </dependency> <!-- https://mvnrepository.com/artifact/com.xuxueli/xxl-job-core --> <dependency> <groupId>com.xuxueli</groupId> <artifactId>xxl-job-core</artifactId> <version>2.4.2</version> </dependency> <!-- https://mvnrepository.com/artifact/co.elastic.clients/elasticsearch-java --> <dependency> <groupId>co.elastic.clients</groupId> <artifactId>elasticsearch-java</artifactId> <version>8.17.1</version> </dependency> <dependency> <groupId>com.luhuiguo</groupId> <artifactId>aspose-words</artifactId> <version>23.1</version> </dependency> <dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>pdfbox</artifactId> <version>3.0.4</version> </dependency> <!-- https://mvnrepository.com/artifact/com.itextpdf/itext-core --> <dependency> <groupId>com.itextpdf</groupId> <artifactId>itext-core</artifactId> <version>9.1.0</version> <type>pom</type> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-pool2 --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> <version>2.12.1</version> </dependency> <dependency> <groupId>io.springboot.ai</groupId> <artifactId>spring-ai-core</artifactId> <version>${spring-ai.version}</version> </dependency> <dependency> <groupId>io.springboot.ai</groupId> <artifactId>spring-ai-ollama</artifactId> <version>${spring-ai.version}</version> </dependency> <!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java --> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-java</artifactId> <version>4.32.0</version> </dependency> <!-- https://mvnrepository.com/artifact/io.milvus/milvus-sdk-java --> <dependency> <groupId>io.milvus</groupId> <artifactId>milvus-sdk-java</artifactId> <version>2.5.10</version> </dependency> <dependency> <groupId>cn.cat</groupId> <artifactId>intelligent-teaching-commons</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>cn.cat</groupId> <artifactId>intelligent-teaching-oss</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>cn.cat</groupId> <artifactId>intelligent-teaching-job</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>cn.cat</groupId> <artifactId>intelligent-teaching-search</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>cn.cat</groupId> <artifactId>intelligent-teaching-ai</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>cn.cat</groupId> <artifactId>intelligent-teaching-chat-shared</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>cn.cat</groupId> <artifactId>intelligent-teaching-exam-shared</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <dependency> <groupId>cn.cat</groupId> <artifactId>intelligent-teaching-user-shared</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-generator --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.5.9</version> </dependency> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-engine-core</artifactId> <version>2.4</version> </dependency> </dependencies> </dependencyManagement> </project>
-
在Java中,当你尝试从Map<String, Object>中移除一个不存在的键时,不会抛出异常。Map接口的remove方法被设计为安全地处理这种情况 - 如果键不存在,方法会简单地返回null(或指定的默认值,如果使用remove(Object key, V defaultValue)方法),而不会抛出任何异常。示例代码:import java.util.HashMap; import java.util.Map; public class Main { public static void main(String[] args) { Map<String, Object> map = new HashMap<>(); map.put("key1", "value1"); map.put("key2", 123); // 尝试移除存在的键 Object removedValue = map.remove("key1"); System.out.println("Removed value: " + removedValue); // 输出: Removed value: value1 // 尝试移除不存在的键 Object nonExistentValue = map.remove("nonExistentKey"); System.out.println("Removed value: " + nonExistentValue); // 输出: Removed value: null // 使用Java 8+的remove方法,可以指定默认值 Object defaultValue = map.remove("anotherNonExistentKey", "default"); System.out.println("Removed value with default: " + defaultValue); // 输出: Removed value with default: default } } 关键点:标准remove方法:map.remove(key) - 如果键存在,返回对应的值;如果键不存在,返回null。带默认值的remove方法(Java 8+):map.remove(key, defaultValue) - 如果键存在,返回对应的值;如果键不存在,返回指定的默认值。不会抛出异常:无论键是否存在,remove方法都不会抛出异常。因此,你可以安全地尝试移除任何键,而不必担心会因为键不存在而导致程序抛出异常。
-
要使用Hutool生成一个包含0到10的随机顺序的数组,你可以按照以下步骤操作:首先创建一个包含0到10的有序数组然后使用ArrayUtil.shuffle方法打乱数组顺序以下是完整的代码示例:import cn.hutool.core.util.ArrayUtil; public class Main { public static void main(String[] args) { // 创建0-10的有序数组 Integer[] array = ArrayUtil.range(0, 11); // 包含0和10 // 打乱数组顺序 ArrayUtil.shuffle(array); // 输出结果 System.out.println(ArrayUtil.toString(array)); } } 代码说明:ArrayUtil.range(0, 11) 创建一个包含0到10(包括0和10)的Integer数组ArrayUtil.shuffle(array) 打乱数组的顺序ArrayUtil.toString(array) 将数组转换为字符串形式输出输出示例:每次运行程序都会得到不同的随机顺序,例如:[5, 2, 9, 0, 7, 10, 3, 1, 4, 6, 8] 注意事项:确保项目中已经引入了Hutool的依赖如果你使用的是基本类型数组(如int[]),需要先转换为Integer[]或使用其他方法ArrayUtil.range方法在Hutool 5.x版本中可用,如果你使用的是较旧版本,可以手动创建数组:Integer[] array = new Integer[11]; for (int i = 0; i < 11; i++) { array[i] = i; } ArrayUtil.shuffle(array);
-
方法一:使用ArrayUtil.shuffle方法import cn.hutool.core.util.ArrayUtil; public class Main { public static void main(String[] args) { Integer[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // 打乱数组顺序 ArrayUtil.shuffle(array); // 输出打乱后的数组 System.out.println(ArrayUtil.toString(array)); } } 方法二:使用ArrayUtil.shuffle方法并指定随机数生成器import cn.hutool.core.util.ArrayUtil; import java.util.Random; public class Main { public static void main(String[] args) { String[] array = {"A", "B", "C", "D", "E"}; Random random = new Random(); // 使用指定的随机数生成器打乱数组 ArrayUtil.shuffle(array, random); System.out.println(ArrayUtil.toString(array)); } } 方法三:对于基本类型数组Hutool也支持基本类型数组的打乱操作:import cn.hutool.core.util.ArrayUtil; public class Main { public static void main(String[] args) { int[] intArray = {1, 2, 3, 4, 5}; // 打乱基本类型数组 ArrayUtil.shuffle(intArray); System.out.println(ArrayUtil.toString(intArray)); } } 注意事项ArrayUtil.shuffle方法会直接修改原始数组,而不是返回一个新的数组对于对象数组和基本类型数组都适用默认使用Random类作为随机数生成器,但可以自定义替代方案如果你不想使用Hutool,也可以使用Java标准库实现:import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Random; public class Main { public static void main(String[] args) { Integer[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // 转换为List后打乱顺序 List<Integer> list = Arrays.asList(array); Collections.shuffle(list); list.toArray(array); // 如果需要,可以转换回数组 System.out.println(Arrays.toString(array)); } } Hutool的ArrayUtil.shuffle方法提供了更简洁的方式来实现数组乱序操作。
-
Java 引入虚拟线程(Virtual Threads)后,确实显著提升了高并发 I/O 密集型任务的性能,使其在并发编程模型上更接近 Go 的 Goroutine,但性能是否与 Go 相当仍需从多个维度综合分析。以下是两者的对比和关键差异:一、虚拟线程 vs. Goroutine:相似性轻量级线程模型虚拟线程:由 JVM 管理,用户态线程,依赖少量操作系统线程(通过协作式调度)。Goroutine:由 Go 运行时管理,用户态线程,依赖 M:N 调度器(用户态线程与操作系统线程的动态映射)。共同点:两者均通过减少线程栈大小和优化调度,支持高并发(数百万级)。同步编程模型虚拟线程:保持同步代码风格(如 try-with-resources 管理执行器)。Goroutine:同样支持同步风格(通过 go 关键字启动协程)。共同点:开发者无需显式处理异步回调,代码更简洁。阻塞行为优化虚拟线程:阻塞时释放底层操作系统线程,避免闲置。Goroutine:类似机制,阻塞时调度器将协程挂起,切换到其他就绪协程。共同点:两者均通过协作式调度减少线程切换开销。二、性能差异分析尽管虚拟线程和 Goroutine 在设计上类似,但实际性能可能因以下因素存在差异:1. 调度器实现Java 虚拟线程:基于 JVM 的调度器,依赖操作系统线程池(如 ForkJoinPool 的变种)。调度策略可能不如 Go 运行时精细(Go 的调度器经过多年优化,对协程的调度效率极高)。Go Goroutine:内置的 M:N 调度器直接集成在运行时中,对协程的挂起、恢复和负载均衡有更精细的控制。调度器能动态调整协程与操作系统线程的映射关系,适应不同场景。2. 内存占用虚拟线程:默认栈大小可通过 -XX:VirtualThreadStackSize 配置(默认 1MB,但可动态压缩)。栈内存占用可能略高于 Goroutine(Go 的栈初始大小通常为 2KB,动态扩展)。Goroutine:极小的初始栈大小(2KB)和高效的动态扩展机制,进一步降低内存开销。3. 启动与销毁开销虚拟线程:创建和销毁速度较快,但仍需 JVM 层面的调度开销。Goroutine:创建和销毁几乎无开销(通过运行时直接管理)。4. 垃圾回收(GC)影响Java:虚拟线程与 JVM 的 GC 紧密集成,GC 暂停可能影响高并发任务的响应时间。Go:并发垃圾回收(Concurrent GC)设计更轻量,对协程的干扰较小。5. 生态与工具链Java:虚拟线程是 Java 21 的新特性,生态工具(如监控、调试)可能需要时间适配。Go:Goroutine 是 Go 的核心特性,生态工具(如 pprof、trace)对其支持完善。三、性能对比的结论I/O 密集型任务虚拟线程:性能接近 Goroutine,尤其在 Java 21 优化后(如减少调度开销、优化栈内存)。Goroutine:仍可能略胜一筹,因其调度器更成熟、内存占用更低。CPU 密集型任务Java:普通线程仍是更优选择(虚拟线程无法直接提升 CPU 计算效率)。Go:Goroutine 适合并发 CPU 任务,但需注意 Goroutine 的调度开销可能影响密集计算。综合性能Go:在轻量级协程、调度效率和生态工具链上仍有优势。Java:虚拟线程显著缩小了差距,尤其在需要与现有 Java 生态(如 Spring、数据库驱动)集成的场景中。四、何时选择 Java 虚拟线程 vs. Go?场景推荐选择原因高并发 I/O 密集型Java 虚拟线程需与现有 Java 生态集成,或团队熟悉 Java。超轻量级协程需求Go Goroutine需要极致的并发性能和更低的内存占用。CPU 密集型任务Java 普通线程Java 的 JIT 优化和原生线程对 CPU 密集型任务更高效。跨平台与生态JavaJava 生态更丰富(如企业级应用、大数据工具)。快速开发与部署GoGo 的编译速度、部署简单性和内置工具链更适合微服务和小型项目。五、总结Java 虚拟线程的引入确实让 Java 在高并发 I/O 密集型场景中具备了与 Go 竞争的能力,但**性能是否“差不多”**取决于具体场景:优势场景:Java 虚拟线程在需要与现有 Java 生态集成的场景中表现优异。Go 的优势:在超轻量级协程、调度效率和生态工具链上仍有领先。未来,随着 JVM 对虚拟线程的进一步优化(如减少调度开销、改进 GC),两者性能差距可能会进一步缩小。但目前,选择仍需根据团队技能、项目需求和生态兼容性综合决定。
-
Java 虚拟线程(Virtual Threads)是 Java 19 引入的一项重大特性(预览版,Java 21 正式发布),旨在简化高并发编程。它与普通线程(也称为平台线程,Platform Threads)在实现机制、资源占用和使用场景上有显著区别。以下是两者的详细对比:一、核心区别维度虚拟线程(Virtual Threads)普通线程(Platform Threads)实现机制由 JVM 管理,用户态线程(User-Mode Threads),依赖操作系统线程池调度。直接映射到操作系统线程(OS Threads),由内核调度。资源占用每个虚拟线程仅占用少量内存(KB 级别),可创建数百万甚至更多。每个普通线程占用大量内存(MB 级别,默认栈大小通常为 1MB),数量受限(通常数千)。调度方式协作式调度(Cooperative Scheduling),由 JVM 调度器在阻塞操作时挂起/恢复。抢占式调度(Preemptive Scheduling),由操作系统内核调度。阻塞行为阻塞时释放底层操作系统线程,避免线程闲置。阻塞时操作系统线程被占用,无法执行其他任务。使用场景高并发 I/O 密集型任务(如 Web 服务器、微服务)。CPU 密集型任务或需要直接操作系统资源的场景。二、深入对比1. 资源占用与并发能力普通线程:每个线程需要独立的操作系统线程,占用大量内存(如栈空间、线程局部存储等)。线程数量受限于操作系统和硬件(通常数千个线程会导致性能下降)。虚拟线程:每个虚拟线程仅占用少量内存(栈大小可通过 -XX:VirtualThreadStackSize 配置,默认 1MB,但可动态压缩)。可轻松创建数百万个虚拟线程,适合高并发场景。2. 调度与阻塞行为普通线程:阻塞操作(如 I/O、网络请求)会导致操作系统线程被占用,无法执行其他任务。线程池(如 ThreadPoolExecutor)通过复用线程缓解此问题,但需手动管理线程池大小。虚拟线程:阻塞时自动释放底层操作系统线程,允许其他虚拟线程复用。阻塞操作完成后,JVM 调度器恢复虚拟线程的执行。3. 性能与吞吐量普通线程:高并发时,线程切换开销大,可能导致 CPU 资源浪费(如线程等待 I/O 时仍占用 CPU)。虚拟线程:减少线程切换开销,提高吞吐量(尤其在 I/O 密集型任务中)。示例:一个普通线程池可能因线程阻塞导致吞吐量下降,而虚拟线程可充分利用 CPU。4. 编程模型普通线程:需显式管理线程生命周期(如 start()、join())。异步编程(如 CompletableFuture)需回调或协程,代码复杂。虚拟线程:保持同步编程模型(如 try (var executor = Executors.newVirtualThreadPerTaskExecutor()))。无需异步回调,代码更简洁。三、代码示例1. 普通线程示例// 使用线程池管理普通线程 ExecutorService executor = Executors.newFixedThreadPool(10); // 线程数量受限 for (int i = 0; i < 1000; i++) { executor.submit(() -> { try { Thread.sleep(1000); // 阻塞操作 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); } executor.shutdown(); 问题:线程池大小固定,高并发时需排队或拒绝任务。2. 虚拟线程示例// 使用虚拟线程执行器(Java 21+) try (var executor = Executors.newVirtualThreadPerTaskExecutor()) { for (int i = 0; i < 1_000_000; i++) { // 轻松创建百万级线程 executor.submit(() -> { try { Thread.sleep(1000); // 阻塞时释放底层线程 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); } } // 自动关闭执行器 优势:无需管理线程池大小,阻塞时自动释放资源。四、适用场景虚拟线程:高并发 I/O 密集型任务(如 Web 服务器、微服务、爬虫)。需要简化异步编程的场景。普通线程:CPU 密集型任务(如科学计算、图像处理)。需要直接操作系统资源(如文件锁、硬件设备)。五、总结特性虚拟线程普通线程资源占用低(KB 级别)高(MB 级别)并发能力高(数百万级)低(数千级)调度方式协作式(JVM 调度)抢占式(内核调度)阻塞行为释放底层线程占用线程编程模型同步(类似单线程)显式线程管理或异步回调适用场景I/O 密集型CPU 密集型或直接操作系统资源六、为什么选择虚拟线程?简化高并发编程:无需异步回调,代码更简洁。资源高效利用:减少线程切换开销,提高吞吐量。弹性扩展:轻松应对突发流量(如 Web 请求)。虚拟线程是 Java 对高并发编程的重大改进,尤其适合现代云原生和微服务架构。然而,对于 CPU 密集型任务,普通线程仍是更优选择。
-
显示文件catalina.sh 必须可执行
-
我使用vue直接前端调用华为云obs上传文件服务传参,在文件管理里面我们需要用到版本控制的功能,需要拿到当前文件的versionId参数,但是我看到了BrowserJS语法没有返回这个参数,其他的都有,这是怎么回事,什么时候能更新一下返回值,或者有什么其他解决办法?
-
smartassist.additionalSettings这个有什么属性可以自己设置的。
-
推送镜像到SWR失败cid:link_0构建任务的日志链接可以对外提供吗?cid:link_1我想问下华为云的这个CodeArts支持CICD吗cid:link_2 CodeArts Snap插件支持哪些客户端和语言cid:link_3CodeArts看板项目如何按照自定义字段进行检索cid:link_4代码托管中组的访问级别如何修改cid:link_5麒麟V10X86架构安装ambari-2.7.5后,利用ambari构建大数据平台报错RuntimeError: Failed to execute command '/usr/bin/yum -y install hadoo cid:link_6麒麟V10安装ambari-server-2.7.5执行ambari-server setup报错Unexpected error Ambari repo file path not set for current OScid:link_7
-
在项目配置中,有个别场景需要通过nacos配置中心来维护一些项目中非spring上下文中的配置,比如:第三方特殊配置、一些非标准化的配置,想通过nacos来实现灵活管理与实时更新。这种需求场景下,我们可以通过Nacos中提供的两个注解来非常简单的实现我们需求。@NacosConfig:需要声明在,由spring管理的bean中,比如:bean的属性上,或者bean的类上。当应用启动时,会将声明了该注解的属性或类,进行赋值。@NacosConfigListener:需要声明在,由spring管理的bean中。作用于Bean的方法上,当Nacos中的配置发生变化时,会以方法入参形式将最新配置内容传入,且支持基本数据类型、对象、泛型类。版本要求:2023.x 系列需升级版本至2023.0.3.22022.x 系列需升级版本至2022.0.0.22021.x 系列需升级版本至2021.0.6.22.2.x 系列需升级至2.2.11@NacosConfig注解用法介绍此注解可作用于bean属性上和类上,前提是需要声明由spring进行管理。支持多种数据类型:基本类型、List、Map等List集合,接收JSON格式配置@Componentpublic class NacosConfigData { @NacosConfig(dataId = "list_demo.json",group = "default_group") private List<MyDemo> myDemoList;}Map泛型,接收JSON格式配置@Componentpublic class NacosConfigData { @NacosConfig(dataId = "map_demo.json",group = "default_group") private Map<Long,MyDemo> myDemoMap;}@NacosConfigListener 注解用法介绍此注解主要作用于方法上,在方法上进行声明,当配置发生变化时,会触发声明了此注解的方法,将最新的配置内容以入参方式传入。自定义bean 接收最新配置@Componentpublic class NacosConfigData { @NacosConfig(dataId = "list_demo.json",group = "default_group") private List<MyDemo> myDemoList; @NacosConfigListener(dataId = "list_demo.json",groupId = "default_group") private void myDemoListChanged(List<MyDemo> myDemoList){ this.myDemoList = myDemoList; } @NacosConfig(dataId = "map_demo.json",group = "default_group") private Map<Long,MyDemo> myDemoMap; @NacosConfigListener(dataId = "map_demo.json",groupId = "default_group") private void myDemoMapChanged(List<MyDemo> myDemoList){ this.myDemoList = myDemoList; }}更为详实介绍,可以查看,参考来源:https://sca.aliyun.com/blog/sca-gvr7dx_awbbpb_xr9f0v45pxz9ubnu/?spm=5176.29160081.0.0.74805c721Hvyc4&source=blog/
-
时间复杂度O是表示算法运行时间与输入数据规模(通常用 n 表示)之间的关系。算法执行时间随输入数据规模增长的变化趋势。1、O(1) — 常数时间无论输入数据多大,执行时间固定不变。典型场景:数组按索引访问、哈希表查询。2、O(log n) — 对数时间执行时间随数据量增长,但增速远慢于线性增长。典型场景:二分查找、平衡二叉搜索树操作。3、O(n) — 线性时间执行时间与数据量成正比。典型场景:遍历数组/链表、线性搜索。4、O(n log n) — 线性对数时间比线性慢,但比平方快,常见于高效排序算法。典型场景:快速排序、归并排序、堆排序。5、O(n²) — 平方时间执行时间与数据量的平方成正比,数据量大时性能急剧下降。典型场景:冒泡排序、选择排序、暴力搜索(如两数之和的暴力解法)。6、O(2ⁿ) — 指数时间执行时间呈指数级增长,仅适用于极小规模数据典型场景:暴力穷举、未优化的递归(如斐波那契数列原始递归)。7、O(n!) — 阶乘时间最慢的时间复杂度,通常用于全排列问题。典型场景:全排列生成。排序从最优到最差:O(1) < O(log n) < O(n) < O(n log n) < O(n^2) < O(2^n) < O(n!)。空间复杂度的表示与时间复杂度的表示基本一致。时间复杂度关注的是运行时间,空间复杂度关注的是内存消耗。现在内存比以前便宜,大家更追求时间的优化了。转载自https://www.cnblogs.com/yanshajiuzhou/p/18829902
-
@ModelAttribute@PostMapping("/register")public String register(@ModelAttribute User user) { // 表单数据将自动绑定到 User 对象中 return "userInfo";}@RequestBody@PostMapping("/api/user")public ResponseEntity<?> saveUser(@RequestBody User user) { // JSON 请求体 {"name":"Tom","age":20} return ResponseEntity.ok(user);}@RequestParam@GetMapping("/search")public String search(@RequestParam String keyword) { // /search?keyword=java return keyword;}@PathVariable@GetMapping("/user/{id}")public String getUser(@PathVariable Long id) { return "ID: " + id;}注意,@RequestBody 需要使用 HttpMessageConverter(如 Jackson、FastJson)支持 JSON 解析。转载自https://www.cnblogs.com/yanshajiuzhou/p/18903319
-
一、创建线程池四种方式使用 Executors 类,Executors 类是 Java 中用于创建线程池的工厂类,它提供了多种静态方法来创建不同类型的线程池使用 ThreadPoolExecutor 类,ThreadPoolExecutor 是 Java 中线程池的一个核心类,它提供了更细粒度的控制来创建和管理线程池使用 Future 和 Callable,Future 和 Callable 是并发编程中非常重要的两个接口,它们通常与 ExecutorService 一起使用来执行异步任务。使用 Spring 的 ThreadPooltaskExecutor,ThreadPoolTaskExecutor 是一个基于 java.util.concurrent.ThreadPoolExecutor 的扩展,提供了更丰富的配置选项和与Spring集成的特性二、线程池重要参数corePoolSize (int): 线程池的基本大小,即在没有任务执行时线程池的大小。当新任务提交时,线程池会优先使用已有的空闲线程。maximumPoolSize (int): 线程池能够容纳同时执行的最大线程数。这个参数用于控制线程池的最大规模,防止因任务过多而导致资源耗尽。keepAliveTime (long): 当线程池中的线程数量超过 corePoolSize 时,多余的空闲线程能等待新任务的最长时间。超过这个时间后,多余的线程将被终止。unit (TimeUnit): keepAliveTime 参数的时间单位,常见的时间单位有 TimeUnit.SECONDS、TimeUnit.MINUTES 等。workQueue (BlockingQueue): 一个阻塞队列,用于存储等待执行的任务。常用的阻塞队列有 LinkedBlockingQueue、ArrayBlockingQueue 和 SynchronousQueue 等。threadFactory (ThreadFactory): 用于创建新线程的工厂。可以通过实现 ThreadFactory 接口来自定义线程的创建过程。handler (RejectedExecutionHandler): 当任务太多而线程池无法处理时,用于定义拒绝任务的策略。常见的拒绝策略有 ThreadPoolExecutor.AbortPolicy、ThreadPoolExecutor.CallerRunsPolicy 和 ThreadPoolExecutor.DiscardPolicy 等。package com.demo.threadPool;import java.util.concurrent.*;public class MainDemo1 { public static void main(String[] args) { int corePoolSize = 5; // 核心线程数 int maximumPoolSize = 10; // 最大线程数 long keepAliveTime = 1; // 非核心线程空闲存活时间 /** * 存活时间单位 * TimeUnit.DAYS:天 * TimeUnit.HOURS:小时 * TimeUnit.MINUTES:分 * TimeUnit.SECONDS:秒 * TimeUnit.MILLISECONDS:毫秒 * TimeUnit.MICROSECONDS:微妙 * TimeUnit.NANOSECONDS:纳秒 */ TimeUnit unit = TimeUnit.MINUTES; BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>(); // 工作队列 ThreadFactory threadFactory = Executors.defaultThreadFactory(); // 线程工厂 RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy(); // 拒绝策略 ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory,handler); }}三、线程池5种状态RUNNING:正常运行状态,可接收新任务,可处理阻塞队列中的任务SHUTDOWN:不会接收新任务,但会处理阻塞队列剩余任务STOP:会中断正在执行的任务,并抛弃阻塞队列任务TIDYING:任务全执行完毕,活动线程为 0,即将进入终结TERMINATED:终结状态四、Executors 类创建线程池new newCachedThreadPool():创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。线程池的规模不存在限制。(数量不固定的线程池)new newFixedThreadPool():创建一个固定长度线程池,可控制线程最大并发数,超出的线程会在队列中等待。(固定数量的线程池)new newScheduledThreadPool():创建一个固定长度线程池,支持定时及周期性任务执行。(定时线程池)new newSingleThreadExecutor():创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。(单线程的线程池)固定线程池创建 ( Executors.newFixedThreadPool(5) ):创建一个固定大小的线程池。线程池中的线程数量是固定的,即使有些线程处于空闲状态,它们也不会被回收。package com.demo.threadPool;import java.util.List;import java.util.concurrent.*;public class MainThreadPool { public static void main(String[] args) throws ExecutionException, InterruptedException { //初始化固定大小线程池 ExecutorService executor1 = Executors.newFixedThreadPool(5); //使用 execute(Runnable command) 方法提交一个不需要返回结果的任务, // 或者使用submit(Callable<T> task) 方法提交一个需要返回结果的任务。 for (int i = 0; i < 10; i++) { executor1.execute(new TaskR(i)); } //使用 submit(Callable<T> task) 任务并获取 Future //使用 Future.get() 方法等待任务完成并获取结果。这个方法会阻塞调用线程直到任务完成。 for (int i = 0; i < 10; i++) { Future<String> future = executor1.submit(new TaskC(i)); System.out.println("线程返回结果 "+future.get()); } // 当所有任务都执行完毕,或者需要关闭线程池时,调用 shutdown() 方法。 // 这将等待正在执行的任务完成,但不接收新任务。 executor1.shutdown(); //使用 shutdownNow() 方法尝试立即停止所有正在执行的任务,并返回等待执行的任务列表 List<Runnable> notExecutedTasks = executor1.shutdownNow(); for(Runnable ls : notExecutedTasks){ System.out.println(ls); } //使用 awaitTermination() 方法等待线程池关闭,直到所有任务完成或超时。 boolean res = executor1.awaitTermination(60, TimeUnit.SECONDS); System.out.println("执行结果:"+res); }}/** * 实现 Runnable 接口 */class TaskR implements Runnable { private int id; public TaskR(int id) { this.id = id; } public void run() { System.out.println("TaskR " + id + " is running..."); }}/** * 实现 Callable 接口 * 有返回值 */class TaskC implements Callable { private int id; public TaskC(int id) { this.id = id; } @Override public Object call(){ System.out.println("TaskC " + id + " is running..."); return id+"--TaskC"; }}单线程池 (newSingleThreadExecutor):创建一个只有一个线程的线程池。即使有多个任务提交,它们也会被排队,逐个由单个线程执行package com.demo.threadPool;import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;/** * 单线程池 (newSingleThreadExecutor): * 创建一个只有一个线程的线程池。即使有多个任务提交,它们也会被排队,逐个由单个线程执行。 */public class MainOne { public static void main(String[] args) throws ExecutionException, InterruptedException { /** * 单线程:创建的执行服务内部有一个线程。所有提交给它的任务将会序列化执行,也就是说,它会在单个线程上依次执行任务,不会有并发执行的情况发生 * 任务队列:如果有多个任务提交给这个执行器,除了当前正在执行的任务外,其他任务将会在一个无界队列中等待,直到线程可用 * 处理任务失败:如果执行中的线程由于任务抛出异常而终止,执行服务会安排一个新的线程来替换它,以继续执行后续的任务 * 使用场景: newSingleThreadExecutor 非常适合需要顺序执行的任务,并且要求任务之间不受并发问题影响的场景 */ ExecutorService executor = Executors.newSingleThreadExecutor(); for (int i = 0; i < 10; i++) { executor.execute(new TaskR(i)); } //使用 submit(Callable<T> task) 任务并获取 Future //使用 Future.get() 方法等待任务完成并获取结果。这个方法会阻塞调用线程直到任务完成。 for (int i = 0; i < 10; i++) { Future<String> future = executor.submit(new TaskC(i)); System.out.println("线程返回结果 "+future.get()); } // 当所有任务都执行完毕,或者需要关闭线程池时,调用 shutdown() 方法。 // 这将等待正在执行的任务完成,但不接收新任务。 executor.shutdown(); }}缓存线程池 (newCachedThreadPool):创建一个可根据需要创建新线程的线程池。如果线程空闲超过60秒,它们将被终止并从池中移除package com.demo.threadPool;import java.util.Date;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/** * 缓存线程池 (newCachedThreadPool): * 创建一个可根据需要创建新线程的线程池。如果线程空闲超过60秒,它们将被终止并从池中移除 */public class MainCacheThreadPool { public static void main(String[] args) throws InterruptedException { System.out.println(Thread.currentThread().getName() + "线程: Start at: " + new Date()); //初始化缓存线程池 ExecutorService exec = Executors.newCachedThreadPool(); for (int i = 1; i < 10; i++) { System.out.println("添加了第" + i + "个任务类"); Thread.sleep(2000); exec.execute(new TaskR(i)); } //所有任务结束后关闭线程池 exec.shutdown(); System.out.println(Thread.currentThread().getName() + " 线程: Finished all threads at:" + new Date()); }}调度线程池 (newScheduledThreadPool):创建一个支持定时任务和周期性任务的线程池package com.demo.threadPool;import java.util.Date;import java.util.concurrent.Executors;import java.util.concurrent.ScheduledExecutorService;import java.util.concurrent.TimeUnit;/** * 固定频率执行 * 调度线程池 (newScheduledThreadPool): * 创建一个支持定时任务和周期性任务的线程池 */public class MainScheduledThreadPool { public static void main(String[] args) { /** * 场景描述 * 假设你需要一个应用程序,该程序能够每10秒执行一次任务,并在启动后1分钟开始执行。此外, * 你还需要能够安排一次性任务在未来的某个时间点执行 */ ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(10); // 安排定期任务 // 初始延迟1分钟,之后每10秒执行一次 threadPool.scheduleAtFixedRate(new TaskR(2), 60, 10, TimeUnit.SECONDS); // 安排一次性任务 // 使用 schedule 方法安排一个任务,在指定的延迟后执行一次 // 延迟5分钟后执行 threadPool.schedule(new TaskR(3), 5, TimeUnit.MINUTES); // 关闭线程池 // 当不再需要线程池时,调用 shutdown 方法来关闭线程池。这将等待正在执行的任务完成,但不接收新任务 threadPool.shutdown(); // 等待线程池关闭 // 使用 awaitTermination 方法等待线程池关闭,直到所有任务完成或超时。 try { threadPool.awaitTermination(1, TimeUnit.HOURS); } catch (InterruptedException e) { e.printStackTrace(); } }}使用给定的线程工厂创建线程池:可以提供一个自定义的 ThreadFactory 来创建线程池中的线程package com.demo.threadPool;import java.util.concurrent.*;/** * 使用给定的线程工厂创建线程池 */public class MainFactory { public static void main(String[] args) { //自定义线程工厂创建 ThreadFactory threadFactory = new ThreadFactory() { @Override public Thread newThread(Runnable r) { return new Thread(r); } }; //使用给定的线程工厂创建线程池 ExecutorService executor = Executors.newFixedThreadPool(5, threadFactory); executor.execute(new TaskR(2)); }}自定义线程工厂创建:自定义线程工厂可以设置自己的线程名,设置守护线程,设置线程优先级,处理未捕获的异常等package com.demo.threadPool;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.ThreadFactory;import java.util.concurrent.atomic.AtomicInteger;/** * 自定义线程工厂:设置线程名,守护线程,优先级以及UncaughtExceptionHandler */public class MainFactory implements ThreadFactory { private final ThreadGroup group; private final AtomicInteger threadNumber = new AtomicInteger(1); private final String namePrefix; public MainFactory(String namePrefix) { SecurityManager s = System.getSecurityManager(); group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(); this.namePrefix = namePrefix + "-thread-"; } public MainFactory(ThreadGroup group, String namePrefix) { this.group = group; this.namePrefix = namePrefix; } @Override public Thread newThread(Runnable r) { Thread t = new Thread(group, r,namePrefix + threadNumber.getAndIncrement(),0); //守护线程 if (t.isDaemon()) t.setDaemon(true); //线程优先级 if (t.getPriority() != Thread.NORM_PRIORITY) t.setPriority(Thread.NORM_PRIORITY); /** * 处理未捕捉的异常 */ t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { System.out.println("处理未捕获的异常"); } }); return t; } //测试方法 public static void main(String[] args) { ExecutorService pool = Executors.newFixedThreadPool(5, new MainFactory("测试线程")); for (int i = 0; i < 10; i++) { pool.execute(new Runnable() { @Override public void run() { System.out.println("线程处理"); //未捕获的异常,走自定义的UncaughtExceptionHandler逻辑 int i = 1 / 0; } }); } pool.shutdown(); }}五、ThreadPoolExecutor 类创建线程池ThreadPoolExecutor 是 java.util.concurrent 包中用来创建线程池的一个类。它提供了一种灵活的方式来管理线程池,允许你控制线程的创建和销毁。1ThreadPoolExecutor 类中的几个重要方法execute():向线程池提交一个任务,交由线程池去执行submit():也是向线程池提交任务,但是和execute()方法不同,它能够返回任务执行的结果它实际上还是调用的 execute() 方法,只不过它利用了 Future 来获取任务执行结果invokeAll():提交一个任务集合invokeAny(): 提交一个任务集合,哪个任务先成功执行完毕,返回此任务执行结果,其它任务取消shutdown():关闭线程池,再也不会接受新的任务不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止shutdownNow():关闭线程池,再也不会接受新的任务立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务isShutdown():不在 RUNNING 状态的线程池,此方法就返回 trueisTerminated():线程池状态是否是 TERMINATEDpackage com.demo.threadPool;import java.util.Random;import java.util.concurrent.*;/** * ThreadPoolExecutor 是 java.util.concurrent 包中用来创建线程池的一个类 * 它提供了一种灵活的方式来管理线程池,允许你控制线程的创建和销毁。 * 以下是几种常见的创建 ThreadPoolExecutor 线程池的方式 * 实际上 Executors 类也是调用 ThreadPoolExecutor 类创建的线程 */public class MainThreadPoolExecutor { //测试方法 public static void main(String[] args) { /** * 核心线程数,核心线程就是一直存在的线程 */ int corePoolSize = 5; /** * 最大线程数,表示线程池中最多能创建多少个线程 * 非核心线程数 = 最大线程数 - 核心线程数 */ int maximumPoolSize = 10; /** * 默认情况下,只有当线程池中的线程数大于corePoolSize时, * keepAliveTime才会起作用,则会终止,直到线程池中的线程数不超过corePoolSize * 则会终止,直到线程池中的线程数不超过corePoolSize * 但是如果调用了 allowCoreThreadTimeOut(boolean) 方法 * 在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为 0 * 针对非核心线程而言,表示线程没有任务执行时最多保持多久时间会终止 */ long keepAliveTime = 60; /** * 时间单位 * 与 keepAliveTime 配合使用,针对非核心线程 */ TimeUnit unit = TimeUnit.SECONDS; /** * 存放任务的阻塞队列 */ BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(5); /** * 创建线程的工厂,可以为线程创建时起个好名字 */ ThreadFactory threadFactory = new ThreadFactory() { @Override public Thread newThread(Runnable r) { return new Thread(r); } }; /** * 拒绝策略 * 任务太多的时候会进行拒绝操作 * 核心线程,非核心线程,任务队列都放不下时 */ // 自定义拒绝策略 RejectedExecutionHandler defaultHandler1 = new MyRejectedExecutionHandler(); // 默认策略,在需要拒绝任务时抛出RejectedExecutionException RejectedExecutionHandler defaultHandler3 = new ThreadPoolExecutor.AbortPolicy(); // 直接在 execute 方法的调用线程中运行被拒绝的任务,如果线程池已经关闭,任务将被丢弃; RejectedExecutionHandler defaultHandler2 = new ThreadPoolExecutor.CallerRunsPolicy(); // 直接丢弃任务 RejectedExecutionHandler defaultHandler4 = new ThreadPoolExecutor.DiscardPolicy(); // 丢弃队列中等待时间最长的任务,并执行当前提交的任务,如果线程池已经关闭,任务将被丢弃 RejectedExecutionHandler defaultHandler5 = new ThreadPoolExecutor.DiscardOldestPolicy(); /** * 创建线程池 */ ExecutorService service1 = new ThreadPoolExecutor( corePoolSize, maximumPoolSize,keepAliveTime, unit,workQueue,threadFactory,defaultHandler1); for (int i = 0; i < 10; i++) { System.out.println("添加第"+i+"个任务"); service1.execute(new MyThread("线程"+i)); } service1.shutdown(); }}/** * 自定义拒绝策略 */class MyRejectedExecutionHandler implements RejectedExecutionHandler { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { new Thread(r,"新线程"+new Random().nextInt(10)).start(); }}/** * 线程类 */class MyThread implements Runnable { String name; public MyThread(String name) { this.name = name; } @Override public void run() { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程:"+Thread.currentThread().getName() +" 执行:"+name +" run"); }}六、Future 和 Callable 类使用创建线程池Callable 是一个函数式接口,它允许你定义一个任务,该任务可以返回一个结果并抛出异常。它是 Runnable 接口的扩展,增加了返回值和抛出异常的能力。返回值:与 Runnable 接口不同,Callable 任务可以返回一个值,返回值通过 Future 对象获取。异常:Callable 任务可以抛出异常,这些异常可以通过 Future 对象处理。Future 接口代表异步计算的结果。它提供了检查计算是否完成的方法,以及获取计算结果的方法。get():获取计算结果。如果计算尚未完成,此方法会阻塞,直到计算完成或抛出异常。isDone():检查计算是否完成。cancel():尝试取消任务。isCancelled():检查任务是否被取消package com.demo.threadPool;import java.util.concurrent.*;/** * Future 使用 */public class MainFuture { public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService executorService = Executors.newFixedThreadPool(1); System.out.println("开始时间戳为:" + System.currentTimeMillis()); Future<String> future = executorService.submit(new Test01()); String result = future.get(); //获取计算结果。如果计算尚未完成,此方法会阻塞,直到计算完成或抛出异常 boolean isdone = future.isDone(); //检查计算是否完成 boolean cancel = future.cancel(true); //尝试取消任务 boolean iscancelled = future.isCancelled(); //检查任务是否被取消 System.out.println("result:"+result); System.out.println("isdone:"+isdone); System.out.println("cancel:"+cancel); System.out.println("iscancelled:"+iscancelled); System.out.println("结束时间戳为:" + System.currentTimeMillis()); executorService.shutdown(); }}class Test01 implements Callable { @Override public Object call() throws Exception { return "你好"; }}七、Spring 的 ThreadPoolTaskExecutor 类创建线程池ThreadPoolTaskExecutor 是 Spring 框架提供的一个线程池实现,它扩展了 Java 的 ThreadPoolExecutor 并提供了一些额外的配置和功能添加依赖: 如果你的项目是一个 Maven 项目,确保你的 pom.xml 文件中包含了 Spring Boot 的依赖配置线程池: 在 Spring Boot 应用程序中,你可以通过 Java 配置类来配置 ThreadPoolTaskExecutorpackage com.cnpc.epai.assetcatalog.dmp.controller;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import java.util.concurrent.ThreadPoolExecutor;/** * 线程池配置类 */@Configurationpublic class ConfigPoolConfiguration { @Bean("TaskExecutorDemo") public ThreadPoolTaskExecutor taskExecutorDemo(){ ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); threadPoolTaskExecutor.setCorePoolSize(10); // 核心线程数 threadPoolTaskExecutor.setMaxPoolSize(20);// 最大线程数 threadPoolTaskExecutor.setQueueCapacity(100); //工作队列 threadPoolTaskExecutor.setKeepAliveSeconds(60); // 非核心线程的空闲存活时间 threadPoolTaskExecutor.setAllowCoreThreadTimeOut(true);//指定是否允许核心线程超时。这允许动态增长和收缩,即使与非零队列结合使用也是如此(因为最大池大小只有在队列已满时才会增长) threadPoolTaskExecutor.setThreadNamePrefix("monitor-thread-pool-");// 设置线程名前缀 threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());// 拒绝策略 threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true);// 设置线程池关闭时需要等待子任务执行完毕,才销毁对应的bean threadPoolTaskExecutor.initialize();//初始化线程池 return threadPoolTaskExecutor; }}测试类package com.cnpc.epai.assetcatalog.dmp.controller;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.scheduling.annotation.Async;import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;import org.springframework.stereotype.Service;@Servicepublic class TestService { @Autowired private ThreadPoolTaskExecutor taskExecutor; @Async("taskExecutor") public void executeTask() { taskExecutor.execute(() -> { System.out.println("Executing task in thread: " + Thread.currentThread().getName()); }); }}———————————————— 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 原文链接:https://blog.csdn.net/weixin_39865508/article/details/140520236
上滑加载中
推荐直播
-
华为云码道 × 仓颉编程:工程化AI编码探索2026/05/27 周三 19:00-21:00
刘俊杰-华为云仓颉语言专家/李炎-华为云码道技术专家/王智鹏-OpenCangjie开源社区发起人
本场直播围绕华为云仓颉语言与华为云码道的深度结合,展示华为云智能编程从零基础到高效落地的完整生态能力。以华为云码道为引擎,仓颉语言为载体,带给大家日常提效、趣味创新到极速量产的开发体验。
回顾中 -
一个AI团队帮你写代码:华为云码道Agent Space实战2026/06/25 周四 19:00-21:00
张翰文-华为云码道工程师/郭英旭-青软创新科技集团股份有限公司 软件架构师
本场直播聚焦华为云码道Agent Space两大模式:研发办公、代码开发,亲身体验从需求到代码的AI自动化能力。实操演示基于华为 CodeArts CLI,依托 OpenSpec 规格体系从零搭建业务项目。
回顾中
热门标签