工作中使用线程池

概要

使用线程池的过程中,直接使用 new 关键字来创建线程(例如 new Thread())是不推荐的做法。这是因为直接创建线程的方式存在一些缺点,而通过线程池可以更有效地管理线程资源,提升应用程序的性能和稳定性。

直接创建线程的缺点

a. 资源消耗大

每次创建一个新线程都需要分配系统资源,尤其是内存资源。频繁创建和销毁线程会导致系统资源的浪费,并可能引起性能问题

b. 线程管理困难

直接创建的线程在使用完之后需要手动管理其生命周期。如果没有正确地管理,可能会导致线程泄漏或其他问题,例如过多的线程占用系统资源。

c. 难以控制并发数量

如果不加控制地创建线程,系统可能会因为过多的并发线程而陷入崩溃状态。线程数的增加会导致上下文切换的开销增大,从而影响系统性能。

使用线程池的优势

a. 资源复用

线程池在初始化时会创建一定数量的线程,这些线程可以复用,而不需要每次都创建新的线程,从而减少了资源消耗。

b. 简化线程管理

线程池会自动管理线程的生命周期,开发者只需要提交任务即可,不需要关心线程的创建和销毁,减少了代码复杂度和错误率。

c. 控制并发数量

线程池可以通过配置最大线程数来控制并发线程的数量,避免了系统因为线程过多而崩溃的问题。

创建线程池给 Spring 管理—示例

以下是一个在工作中常用的、由Spring管理的线程池配置和使用的完整案例。这段代码展示了如何在Spring Boot应用中创建和配置线程池,并使用它来执行异步任务。

创建线程池配置类

通过创建一个配置类来定义线程池:

@Configuration
public class ThreadPoolConfig {
    /**
     * 定义一个名为 "taskExecutor" 的线程池Bean
     */
    @Bean(name = "taskExecutor")
    public Executor taskExecutor() {
        // 创建ThreadPoolTaskExecutor实例
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        
        // 设置核心线程数,即线程池在闲置状态下的最小线程数
        executor.setCorePoolSize(5);
        
        // 设置最大线程数,即线程池在繁忙状态下的最大线程数
        executor.setMaxPoolSize(10);
        
        // 设置队列容量,当有超过核心线程数的任务提交时,这些任务会被放在队列中等待执行
        executor.setQueueCapacity(25);
        
        // 设置线程名前缀,方便在日志和监控中区分线程池中的线程
        executor.setThreadNamePrefix("MyThreadPool-");
        
        // 初始化线程池
        executor.initialize();
        
        // 返回配置好的线程池实例
        return executor;
    }
}

使用线程池

在Spring Bean中注入线程池并使用它来执行任务:

@Service
public class MyService {
	//@Async("taskExecutor"):这个注解表示该方法将在异步执行。
	//"taskExecutor"是我们定义的线程池名称,这意味着这个方法将由taskExecutor线程池中的线程来执行,而不是由调用线程执行。
	//这个机制允许任务在后台执行,从而不会阻塞调用者的线程。
    @Async("taskExecutor")
    public CompletableFuture<String> executeTask(String taskName) {
        System.out.println(Thread.currentThread().getName() + " - Executing task: " + taskName);
        // 模拟任务处理
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return CompletableFuture.completedFuture("Task " + taskName + " completed");
    }
}

启用异步支持

在Spring配置类中启用异步方法调用:

@Configuration
@EnableAsync
public class AsyncConfig {
}

创建控制器调用服务

在控制器中调用服务方法来执行任务:

@RestController
public class MyController {

    @Autowired
    private MyService myService;

    @GetMapping("/execute")
    public String executeTask(@RequestParam String taskName) {
        CompletableFuture<String> future = myService.executeTask(taskName);
        return "Task " + taskName + " is being executed.";
    }
}

运行应用

确保你的Spring Boot应用程序可以正常运行。启动应用后,你可以通过访问以下URL来测试线程池:

http://localhost:8080/execute?taskName=test

总结

通过上述步骤,你可以在Spring Boot应用中创建和使用线程池,并将其配置成由Spring管理。这样可以方便地管理并发任务,提高应用的性能和响应能力。

Future介绍

Future 是 Java 中用于表示异步计算结果的接口。它的主要功能是允许你在任务还未完成时获取结果,而不是阻塞等待任务完成。Future 提供了一些方法来检查任务是否完成、等待任务完成并获取结果,以及取消任务等。

Future 接口的主要方法

Future 接口在 java.util.concurrent 包中定义,包含以下主要方法:

  • boolean cancel(boolean mayInterruptIfRunning):尝试取消任务。如果任务已经完成或已经被取消,则返回 false;如果任务成功取消,则返回 true
  • boolean isCancelled():如果任务在完成前被取消,则返回 true
  • boolean isDone():如果任务已完成,则返回 true
  • V get():等待任务完成并获取结果,如果任务未完成,则阻塞直到任务完成。
  • V get(long timeout, TimeUnit unit):等待任务完成并获取结果,但只等待指定的时间。如果在超时时间内任务未完成,则抛出 TimeoutException

示例代码

public class FutureTest {
    public static void main(String[] args) {
        // 创建线程池
        ExecutorService executor = Executors.newFixedThreadPool(2);

        // 提交任务
        Future<Integer> future = executor.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                // 模拟长时间任务
                Thread.sleep(2000);
                return 123456;
            }
        });

        // 检查任务是否完成
        if (!future.isDone()) {
            System.out.println("Task is not completed ...");
        }

        // 获取任务结果
        try {
            Integer result = future.get(); // 这将阻塞直到任务完成
            System.out.println("Task completed!! Result: " + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

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

CompletableFuture

CompletableFuture 是 Java 8 引入的一个更高级的类,它实现了 Future 接口,并提供了更多功能,如流式处理和组合多个异步任务。CompletableFuture 允许更灵活的方式来处理异步计算,支持非阻塞、回调、异常处理等。

CompletableFuture 示例

public class CompletableFutureTest {
    public static void main(String[] args) {
        // 创建一个CompletableFuture
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            // 模拟长时间任务
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 123321;
        });

        // 添加回调
        future.thenAccept(result -> System.out.println("Task completed!! Result: " + result));

        // 等待任务完成
        try {
            future.get(); // 阻塞等待
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}
  • runAsync 适用于没有返回值的任务,返回 CompletableFuture。
CompletableFuture.runAsync(()->function());
  • supplyAsync 适用于有返回值的任务,返回 CompletableFuture,其中 T 是任务返回的结果类型。

总结

  • Future 接口用于表示异步计算的结果,可以检查任务是否完成、获取结果或取消任务。
  • CompletableFutureFuture 的扩展,提供了更丰富的功能,如流式处理、组合多个异步任务、处理异常等。它更适合于现在Java应用中处理异步任务。

❤觉得有用的可以留个关注~~❤

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
线程池是一种常见的并发编程技术,可以在程序重复使用一组线程,以减少线程创建和销毁的开销,提高程序的性能和稳定性。 在Java,可以使用Java自带的线程池类ThreadPoolExecutor来实现线程池使用线程池的基本步骤如下: 1. 创建线程池对象。可以使用ThreadPoolExecutor类的构造函数来创建线程池,需要指定线程池的核心线程数、最大线程数、线程空闲时间、工作队列等参数。 2. 创建任务对象。可以创建Runnable或Callable接口的实现类来表示要执行的任务。 3. 提交任务。可以使用线程池对象的execute()方法或submit()方法来提交任务,线程池会自动从线程池获取空闲线程来执行任务。 4. 关闭线程池。在程序结束时,需要显式地调用线程池的shutdown()方法来关闭线程池,释放线程池线程和资源。 下面是一个使用线程池的示例代码: ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolDemo { public static void main(String[] args) { // 创建线程池对象,使用FixedThreadPool类型 ExecutorService executor = Executors.newFixedThreadPool(5); // 提交任务 for (int i = 0; i < 10; i++) { executor.execute(new Task(i)); } // 关闭线程池 executor.shutdown(); } static class Task implements Runnable { private int id; public Task(int id) { this.id = id; } @Override public void run() { System.out.println("Task " + id + " is running on thread " + Thread.currentThread().getName()); } } } ``` 在这个示例代码,创建了一个FixedThreadPool类型的线程池,最多可以有5个线程同时执行任务。提交了10个任务,每个任务都是一个Runnable接口的实现类。任务执行时,会打印出任务的编号和执行线程的名字。最后,调用了线程池的shutdown()方法来关闭线程池

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值