【Java教程】Day15-17 多线程:线程池与定时任务

在Java中,多线程编程是十分常见且方便的操作,但当需要频繁创建和销毁线程时,由于每个线程的创建都会消耗操作系统资源(如线程栈等),这会导致性能问题。为了提高线程的复用率,避免频繁创建和销毁线程的开销,线程池作为一种有效的解决方案应运而生。

 

1. 什么是线程池?

线程池可以理解为一组线程集合,用于执行大量的任务。与每个任务对应一个新线程的做法不同,线程池通过复用一组线程来处理多个任务,从而提高了程序的性能和资源利用率。线程池的工作流程如下图所示:


 
lua┌─────┐ execute  ┌──────────────────┐│Task1│─────────▶│ThreadPool        │├─────┤          │┌───────┐┌───────┐││Task2│          ││Thread1││Thread2││├─────┤          │└───────┘└───────┘││Task3│          │┌───────┐┌───────┐│├─────┤          ││Thread3││Thread4│││Task4│          │└───────┘└───────┘│├─────┤          └──────────────────┘│Task5│├─────┤│Task6│└─────┘

 

当有任务需要执行时,线程池会从池中选择一个空闲线程来执行任务。如果所有线程都在忙碌,新的任务就会被放入队列中,等待线程空闲后执行。对于高并发任务,线程池还可以通过增加线程数量来进行处理。

2. Java中线程池的实现

在Java中,线程池通过ExecutorService接口来实现。Java标准库提供了多种线程池实现,可以根据不同的需求选择合适的类型。最常见的线程池有:

  • FixedThreadPool:线程数固定的线程池。

  • CachedThreadPool:线程数根据任务数量动态调整的线程池。

  • SingleThreadExecutor:只有一个线程执行的线程池。

2.1 创建固定大小的线程池

下面是如何使用ExecutorService创建一个固定大小的线程池,并提交任务执行:


 
java// 创建一个固定大小的线程池ExecutorService executor = Executors.newFixedThreadPool(3);// 提交任务executor.submit(new Task("Task1"));executor.submit(new Task("Task2"));executor.submit(new Task("Task3"));executor.submit(new Task("Task4"));executor.submit(new Task("Task5"));// 关闭线程池executor.shutdown();

 

这里,线程池的大小为3,因此最多只能同时执行3个任务。其余任务将排队等待。

2.2 线程池的执行逻辑

以下是一个示例程序,演示了如何使用FixedThreadPool线程池执行多个任务:


 
javaimport java.util.concurrent.*;public class Main {    public static void main(String[] args) {        // 创建一个固定大小的线程池        ExecutorService es = Executors.newFixedThreadPool(4);        // 提交多个任务        for (int i = 0; i < 6; i++) {            es.submit(new Task("" + i));        }        // 关闭线程池        es.shutdown();    }}class Task implements Runnable {    private final String name;    public Task(String name) {        this.name = name;    }    @Override    public void run() {        System.out.println("Start task " + name);        try {            Thread.sleep(1000); // 模拟任务执行        } catch (InterruptedException e) {            Thread.currentThread().interrupt();        }        System.out.println("End task " + name);    }}

 

执行结果分析:

  • 总共有6个任务提交给线程池,但是线程池的大小是4,所以最多同时执行4个任务。

  • 任务会按照线程池空闲线程的情况排队执行。

2.3 关闭线程池

使用线程池时,必须在程序结束时调用shutdown()方法关闭线程池。shutdown()方法会等到所有正在执行的任务完成后再关闭线程池。如果希望立刻停止正在执行的任务,可以使用shutdownNow()


 
java// 优雅关闭线程池,等待任务完成executor.shutdown();// 强制关闭线程池,停止所有任务executor.shutdownNow();

 

3. 线程池的动态调整

如果需要根据任务量动态调整线程池的大小,可以使用ThreadPoolExecutor。下面是如何创建一个动态大小的线程池:


 
javaint min = 4;  // 最小线程数int max = 10; // 最大线程数ExecutorService es = new ThreadPoolExecutor(        min, max,        60L, TimeUnit.SECONDS,        new SynchronousQueue<Runnable>());

 

ThreadPoolExecutor通过调整线程池大小来处理不同数量的任务,可以在多任务高并发情况下提供更好的性能。

4. 定时任务:ScheduledThreadPool

有时,我们需要执行一些定期任务,如定时刷新数据。Java中的ScheduledThreadPool能够帮助我们解决这个问题。ScheduledThreadPool可以执行定期任务和延迟任务。

4.1 一次性任务

可以使用schedule()方法提交一次性任务,指定延迟后执行:


 
javaScheduledExecutorService ses = Executors.newScheduledThreadPool(4);ses.schedule(new Task("one-time"), 1, TimeUnit.SECONDS);

 

4.2 固定间隔执行任务

如果任务需要固定间隔执行,可以使用scheduleAtFixedRate()scheduleWithFixedDelay()方法。

  • FixedRate:任务总是以固定时间间隔触发,不考虑任务执行时间。例如,每隔3秒执行一次。


 

 

javases.scheduleAtFixedRate(new Task("fixed-rate"), 2, 3, TimeUnit.SECONDS);

 

  • FixedDelay:任务执行完毕后,等待固定时间间隔再执行下一次。例如,每隔3秒执行一次,但会等待上一次任务完成后再开始执行。


 

 

javases.scheduleWithFixedDelay(new Task("fixed-delay"), 2, 3, TimeUnit.SECONDS);

 

5. 定时任务与Timer类的比较

在Java中,还有一个Timer类也能实现定时任务功能。但与ScheduledThreadPool不同,Timer每次只能调度一个任务,而ScheduledThreadPool可以同时调度多个任务。因此,ScheduledThreadPoolTimer的更优选择。

6. 练习

通过使用线程池复用线程,提高任务执行效率。实现一个线程池,并尝试提交不同类型的任务,观察线程池如何管理线程和任务。

7. 小结

  • 线程池的优势:线程池通过复用一组线程来处理多个任务,避免了频繁创建和销毁线程的性能开销。

  • 线程池类型FixedThreadPoolCachedThreadPoolSingleThreadExecutor等。

  • 定时任务调度ScheduledThreadPool支持定期执行任务,适用于需要定时执行的场景。

  • 线程池的关闭:必须在应用程序结束时关闭线程池,以释放资源。

 

通过合理使用线程池,能够有效提升Java程序在并发环境下的执行效率,并且在需要定时任务时,ScheduledThreadPool提供了方便的定时任务调度能力。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值