Java 学习笔记(二)

CountDownLatch

CountDownLatch 是 Java 并发包 java.util.concurrent 中的一个类,它用于协调一个或多个线程,允许一个或多个线程等待一组在其他线程中执行的操作完成。CountDownLatch 是一种同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。

主要特性和用法
构造函数:
CountDownLatch(int count):创建一个新的 CountDownLatch 实例,给定的计数 count 是必须在 await() 方法之前降至零的线程数。
主要方法:
void countDown():递减锁存器的计数,如果计数到达零,则释放所有等待的线程。如果当前计数大于零,则递减,然后如果新计数为零,将唤醒所有等待的线程。
void await() throws InterruptedException:使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。如果当前计数为零,则此方法立即返回。
boolean await(long timeout, TimeUnit unit) throws InterruptedException:使当前线程在锁存器倒计数至零之前等待,除非线程被中断或超出了指定的等待时间。如果在等待时间内计数到达零,则返回 true;如果在所有等待线程都已中断,或者已超出指定的等待时间后计数仍未到达零,则返回 false。
使用场景
CountDownLatch 常用于以下场景:

在主线程中等待多个任务线程完成后再继续执行。
在启动服务时,等待所有必要的资源或服务初始化完成后再继续执行。
在执行批量操作时,等待所有子任务完成后进行汇总处理。
示例代码
以下是一个使用 CountDownLatch 的简单示例,演示了如何在主线程中等待多个任务线程完成后再继续执行。

java
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
// 初始化 CountDownLatch,计数为 3,表示需要等待 3 个任务完成
CountDownLatch latch = new CountDownLatch(3);

    // 创建一个线程池  
    ExecutorService executor = Executors.newFixedThreadPool(3);  

    // 提交三个任务到线程池  
    for (int i = 0; i < 3; i++) {  
        int taskId = i;  
        executor.submit(() -> {  
            // 模拟任务执行  
            System.out.println("任务 " + taskId + " 正在执行");  
            try {  
                Thread.sleep(1000); // 假设每个任务需要 1 秒来完成  
            } catch (InterruptedException e) {  
                Thread.currentThread().interrupt();  
            }  
            System.out.println("任务 " + taskId + " 执行完成");  
            // 任务完成后,计数器减一  
            latch.countDown();  
        });  
    }  

    // 等待所有任务完成  
    latch.await();  

    // 所有任务都完成后,主线程继续执行  
    System.out.println("所有任务执行完成,主线程继续执行");  

    // 关闭线程池  
    executor.shutdown();  
}  

}
在这个示例中,主线程通过调用 latch.await() 等待三个任务线程完成。每个任务线程在完成任务后调用 latch.countDown() 来减少计数器的值。当计数器的值减至零时,latch.await() 返回,主线程继续执行。

ThreadPoolTaskExecutor

ThreadPoolTaskExecutor是Spring框架提供的一个线程池实现,它基于Java的java.util.concurrent.ThreadPoolExecutor类进行封装,并提供了在Spring应用程序中创建和配置线程池的便捷方式。以下是ThreadPoolTaskExecutor的详细介绍:

一、主要特点
线程池管理:ThreadPoolTaskExecutor可以管理线程的生命周期,包括线程的创建、执行、调度和销毁,从而避免不必要的线程创建和销毁开销。
性能提升:通过复用线程和限制并发线程的数量,ThreadPoolTaskExecutor可以提高程序的性能和资源利用率。
灵活配置:允许开发者根据需要配置核心线程数、最大线程数、队列容量等线程池属性,以及线程优先级、拒绝策略等高级特性。
生命周期管理:与Spring的生命周期集成,可以在上下文关闭时优雅地关闭线程池。
二、配置参数
ThreadPoolTaskExecutor提供了多个配置参数,以满足不同的需求:

核心线程数(Core Pool Size):线程池中的核心线程数,即使这些线程处于空闲状态,也不会被销毁。
最大线程数(Maximum Pool Size):线程池中允许的最大线程数。
队列容量(Queue Capacity):当线程池中的线程数达到核心线程数时,新任务将被放入队列中等待执行。队列的容量决定了能够缓存多少等待执行的任务。
线程存活时间(Keep-Alive Time):当线程池中的线程数超过核心线程数时,多余的空闲线程在终止前等待新任务的最长时间。
线程名称前缀(Thread Name Prefix):用于生成线程名称的前缀,便于在日志或监控中识别线程。
拒绝策略(Rejected Execution Handler):当线程池和队列都满时,用于处理新任务的拒绝策略。常见的拒绝策略包括抛出异常、由调用者线程执行、丢弃任务和丢弃最老的任务。
三、使用示例
以下是一个使用ThreadPoolTaskExecutor的示例代码:

java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@Configuration
public class ThreadPoolConfig {

@Bean  
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {  
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();  
    executor.setCorePoolSize(10); // 核心线程数  
    executor.setMaxPoolSize(20); // 最大线程数  
    executor.setQueueCapacity(50); // 队列容量  
    executor.setThreadNamePrefix("MyExecutor-"); // 线程名称前缀  
    // 设置拒绝策略,这里使用默认的AbortPolicy  
    // executor.setRejectedExecutionHandler(...);  
    executor.initialize(); // 初始化线程池  
    return executor;  
}  

}

// 在需要的地方注入并使用ThreadPoolTaskExecutor
@Autowired
private ThreadPoolTaskExecutor threadPoolTaskExecutor;

// 提交任务给线程池执行
threadPoolTaskExecutor.execute(() -> {
// 任务执行逻辑
});
四、注意事项
在使用ThreadPoolTaskExecutor时,应根据实际应用场景和需求合理配置线程池参数,以避免资源浪费或性能瓶颈。
注意线程池的生命周期管理,确保在应用程序关闭时能够优雅地关闭线程池,释放资源。
在选择拒绝策略时,应根据业务需求和系统容错能力进行选择,确保系统的稳定性和可靠性。
总之,ThreadPoolTaskExecutor是Spring框架中用于管理线程池的一个重要组件,它提供了灵活的配置选项和便捷的使用方式,能够帮助开发者更好地管理多线程任务,提高程序的性能和资源利用率。

@EnableAsync、@Async

@EnableAsync 和 @Async 是 Spring 框架中用于支持异步方法执行的注解。这两个注解配合使用,可以使得在 Spring 应用程序中的方法异步执行,而不会阻塞调用线程。这对于提高应用程序的响应性和吞吐量非常有用,尤其是在处理耗时的操作(如文件IO、网络请求、复杂计算等)时。

@EnableAsync
@EnableAsync 是一个配置注解,用于开启 Spring 的异步方法执行能力。当你将 @EnableAsync 注解添加到你的配置类(通常是使用 @Configuration 注解的类)上时,Spring 会自动查找标注了 @Async 的方法,并在执行这些方法时,使用 Spring 的 TaskExecutor 实现(通常是 ThreadPoolTaskExecutor)来异步执行它们。

@Async
@Async 是一个方法级别的注解,用于标记一个方法应该被异步执行。当你将一个方法标记为 @Async 时,Spring 会在调用该方法时,自动在一个新的线程中执行该方法,而不会阻塞调用它的线程。

参数
@EnableAsync
@EnableAsync 注解本身不直接接受任何参数,但它是通过配置 TaskExecutor 来控制异步方法的执行细节的。TaskExecutor 的配置可以在配置类中通过 Java 配置(如使用 @Bean 注解定义 ThreadPoolTaskExecutor)或通过 XML 配置来完成。

@Async
@Async 注解同样不直接接受参数来控制异步方法的执行行为,但它有一些与异步执行相关的注意事项和限制:

返回值:异步方法可以有返回值,但通常这些值是通过 Future 或 CompletableFuture 来获取的,因为调用线程不会等待异步方法完成。
异常处理:异步方法抛出的异常默认不会在调用线程中传播。如果需要对异常进行处理,可以通过在异步方法内部捕获异常,或者通过 Future.get() 方法(这会阻塞直到异步方法完成)来捕获 ExecutionException。
事务管理:默认情况下,@Async 方法不会参与调用者的事务。如果需要在异步方法中管理事务,需要额外的配置和注意。
参数传递:异步方法可以接受参数,并且这些参数的传递方式与同步方法相同。但是,如果传递的是可变对象或状态共享的对象,需要小心线程安全问题。
示例
java
@Configuration
@EnableAsync
public class AsyncConfig {

@Bean  
public Executor taskExecutor() {  
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();  
    executor.setCorePoolSize(2);  
    executor.setMaxPoolSize(2);  
    executor.setQueueCapacity(500);  
    executor.setThreadNamePrefix("GithubLookup-");  
    executor.initialize();  
    return executor;  
}  

}

@Service
public class AsyncService {

@Async  
public CompletableFuture<String> findGithubUser(String userName) {  
    // 模拟耗时操作  
    return CompletableFuture.completedFuture("User: " + userName);  
}  

}
在这个例子中,AsyncConfig 类通过 @EnableAsync 注解开启了异步支持,并配置了一个 ThreadPoolTaskExecutor。AsyncService 类中的 findGithubUser 方法被 @Async 注解标记为异步方法,它返回一个 CompletableFuture 对象,调用者可以通过这个对象来获取异步方法的结果。

@EnableAsync

@EnableAsync 注解在 Spring 框架中用于开启对异步方法的支持。当你使用 @Async 注解标记一个方法时,Spring 会自动将这个方法的执行提交到一个任务执行器(TaskExecutor)上,默认情况下是 SimpleAsyncTaskExecutor,但它不支持真正的并发执行(因为它每次调用都会创建一个新的线程)。

如果你想要控制并发执行的细节,比如线程池的大小、线程存活时间等,你通常会配置一个自定义的 ThreadPoolTaskExecutor(它是 TaskExecutor 的一个实现,内部使用了 ThreadPoolExecutor)。

即使你使用了自定义的 ThreadPoolTaskExecutor,@EnableAsync 注解仍然是必要的。这个注解告诉 Spring 框架要查找标有 @Async 的方法,并将它们的执行委托给配置的异步执行器(无论是默认的还是自定义的)。

简而言之,@EnableAsync 注解是启用异步方法处理的关键,而自定义的 ThreadPoolTaskExecutor(或任何其他 TaskExecutor 实现)则用于控制这些异步方法的实际执行方式。

以下是一个简化的配置流程:

在你的配置类上添加 @EnableAsync 注解。
定义一个 ThreadPoolTaskExecutor 的 Bean,并对其进行配置。
在需要异步执行的方法上使用 @Async 注解,并指定(可选)执行器名称(如果你配置了多个执行器)。
示例配置:

java
@Configuration
@EnableAsync
public class AsyncConfig {

@Bean(name = "taskExecutor")  
public Executor taskExecutor() {  
    ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();  
    executor.setCorePoolSize(5);  
    executor.setMaxPoolSize(10);  
    executor.setQueueCapacity(25);  
    executor.initialize();  
    return executor;  
}  

}

@Service
public class AsyncService {

@Async("taskExecutor")  
public void executeAsyncTask() {  
    // 异步执行的代码  
}  

}
在这个例子中,@EnableAsync 注解启用了对 AsyncService 类中 @Async 注解方法的支持,而自定义的 ThreadPoolTaskExecutor 则定义了这些异步方法将如何被执行。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

路上阡陌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值