讨论Java线程池如何工作以及如何在你的应用程序中使用它们

42 阅读6分钟

在计算中,线程池由一组预先分配的线程组成,它们善于按需执行任务。使用线程池可以极大地减少资源消耗,因为应用程序不会在每次需要线程的时候创建一个新的线程。

相反,一个准备好的--或可运行的--线程(来自线程池),因为它被称为,被分配了要执行的任务,新任务的执行随后发生。在运行时,一个任务被分配给线程池中的一个线程,然后执行。

在这个Java编程教程中,我们将讨论Java线程池如何工作以及如何在你的应用程序中使用它们。

什么是线程池?

顾名思义,线程池由一组准备在必要时运行的线程组成。线程池的大小和数量取决于你希望能够同时运行多少任务。

线程池也可用于以受控方式执行任务;例如,通过限制并发执行的数量或将某些任务优先于其他任务。此外,线程池可以使用一个任务队列,这可以帮助确保关键任务不会因为缺乏可用的线程而被延迟。

开发人员为什么要使用线程池?

在某些情况下,拥有多个执行线程是很有用的。例如,开发人员可能想执行一个独立于其应用程序或服务的主线程的后台任务。在这种情况下,程序员可以为他们的后台任务使用一个单独的线程,而不必担心阻断主线程的执行。

然而,创建新的线程有相关的开销,在处理大量数据时,会导致内存使用量增加,执行速度变慢。另外,考虑到每次在线程之间切换(也称为上下文切换)时,由于上下文切换、CPU缓存刷新以及在幕后进行的其他一些事情,如将不同的堆栈加载到内存中,等等,都会对性能产生影响。

如果上下文切换很频繁和/或这些缓存被刷新得太频繁,那么性能就会大打折扣,因为在这些缓存中等待的时间会多于实际做有用的工作。线程池允许开发者避免这两个问题,因为它允许我们控制在任何特定时间使用多少线程,同时也管理它们的生命周期。

线程池可以提高应用程序的性能,因为它允许任务并发执行,并提供一种机制来控制在任何给定时间内活动的线程数量。使用线程池可以大大降低应用程序所需的线程数量,从而降低资源消耗并大大改善性能。

使用线程池的优势是什么

在编写Java代码时,使用线程池比自己创建和管理线程有很多优势。

线程池可以通过重用线程和避免每次执行任务时创建新线程的开销,来帮助提高应用程序的性能。它们还可以通过排队并在有线程可用时立即执行任务来确保任务及时执行。

使用线程池的另一个好处是,它们可以使你的代码更加健壮,允许你优雅地处理需要执行的任务多于可用线程的情况。在这种情况下,线程池将简单地排队等待任务,直到有线程可用来执行它们。

如何在Java中创建一个线程池

java.util.concurrent包提供了几个可用于此目的的类,包括Executors类和ThreadPoolExecutor类。

使用Executor类是创建线程池的最简单方法,但ThreadPoolExecutor类提供了更多的灵活性和控制。

请参考下面给出的代码清单,它显示了你如何使用ExecutorService类来创建Java中的线程池:

import java.util.concurrent.*;
public class MyThreadPool {
public static void main(String[] args) {
// Create a fixed-size thread pool with three threads
ExecutorService executorService = Executors.newFixedThreadPool(3);
// Submit a task to the executor
executorService.submit(new Runnable() {
public void run() {
// Write your custom code here...
System.out.println("Inside run method...");
}
});
executorService.shutdown(); // Shut down the executor service
}
}

ThreadPoolExecutor类的实例可以使用其静态工厂方法之一来创建,如newFixedThreadPool()newCachedThreadPool()。一旦创建,ThreadPoolExecutor就可以通过调用其execute()方法来执行任务。注意,提交给ThreadPoolExecutor的任务必须是RunnableCallable接口的实例。

请参考下面给出的代码清单,它显示了如何使用ThreadPoolExecutor类来创建Java中的线程池:

import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class MyThreadPool 
{
    public static void main(String[] args) 
    {
        ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);
       for (int i = 1; i <= 5; i++) 
        {
            MyTask task = new MyTask("Task " + i);
            threadPoolExecutor.execute(task);
        }
        threadPoolExecutor.shutdown();
    }
}
public class MyTask implements Runnable {
    public void run() {
       System.out.println("Executing the run() method...");
    }
}

线程池的用例

线程池通常用于服务器应用程序,通过创建一个线程池(有一个最大阈值)来提高性能,该线程池可用于按需服务请求,而不是为每个请求创建一个新线程。

例如,一个Web服务器利用线程池来服务请求。当一个新的请求到来时,Web服务器可以创建一个新的线程来处理该请求。通过使用线程池,Web服务器可以确保总是有足够的线程来处理进入的请求。

何时不使用线程池

如果你的应用程序不处理很多线程,你可以避免使用线程池。创建和销毁线程需要时间,所以在这种情况下使用线程池只会增加开销,没有明显的好处。此外,线程池本身也会消耗资源。

开发者不想使用线程池的另一种情况是,如果你的应用程序需要线程来执行不相关的操作。例如,当一个线程处理用户事件时,另一个线程执行业务逻辑,还有一个线程打印数据。

如果程序员的应用程序会被长时间阻塞,就不应该使用线程池。在这种情况下,你不应该使用线程池,因为,如果有太多的阻塞线程,任务根本就不会启动。

关于线程池的最终想法

线程池是一种很好的方式,可以通过重用线程和避免为每个任务创建新线程的开销来提高Java应用程序的响应速度。线程池不仅为一个或多个线程预先分配了资源,而且还限制了在某个时间点上使用的线程数量。