Java中单线程与单线程执行器服务的比较
Java中单线程与单线程执行器服务的比较
线程和执行器框架是Java中用于并行执行代码的两种机制。这提高了应用程序的性能。执行器框架提供了不同类型的线程池。其中一种池只包含一个工作线程。
在本教程中,我们将学习线程和具有单个工作线程的执行器服务之间的区别。
线程是一个轻量级进程,具有独立的执行路径。它用于并行执行任务。因此,可以同时运行多个线程而互不干扰。
一个_Thread_对象执行_Runnable_任务。
让我们看看如何创建线程。我们可以通过扩展_Thread类_或实现_Runnable_接口来创建线程。**
让我们通过扩展_Thread_类来创建一个线程:
public class CustomThread extends Thread {
// 重写run()方法以提供自定义实现
public static void main(String[] args) {
CustomThread t1 = new CustomThread();
t1.start();
}
}
在上面的例子中,_CustomThread_类扩展了_Thread_类。在_main()_方法中,我们创建了_CustomThread_类的实例,然后调用了它的_start()_方法。这开始了线程的执行。
现在让我们看看通过实现_Runnable_接口来创建线程的例子:
public class TestClass implements Runnable {
// 实现Runnable接口的run()方法
public static void main(String[] args) {
TestClass testClassRef = new TestClass();
Thread t1 = new Thread(testClassRef);
t1.start();
}
}
在上面的例子中,_TestClass_实现了_Runnable_接口。我们将_TestClass_对象的引用传递到_Thread_类的构造函数中。然后,我们调用_start()_方法。这反过来调用由_TestClass_实现的_run()_方法。
3. 执行器框架
现在我们将学习执行器框架。它在JDK 1.5中引入。**它是一个多线程框架,维护了一个工作线程池并管理它们。**任务提交到队列中,然后由这些工作线程执行。
它消除了在代码中显式创建线程的开销。相反,它重用了池中的线程来异步执行任务。
让我们现在看看执行器框架维护的不同种类的线程池。
3.1. 固定线程池
**这个池包含一个固定数量的线程。**我们在创建池时指定线程数量。如果发生异常并且一个线程被终止,将创建一个新的线程。
让我们看看如何创建一个固定线程池:
ExecutorService executorService = Executors.newFixedThreadPool(5);
在上面的代码片段中,我们创建了一个有五个工作线程的固定线程池。
3.2. 缓存线程池
**这个线程池在需要时创建新线程。**如果没有线程可用于执行提交的任务,那么将创建一个新线程。
这是我们如何创建一个缓存线程池:
ExecutorService executorService = Executors.newCachedThreadPool();
在缓存线程池中,我们不指定池大小。这是因为它在没有可用线程执行提交的任务时创建新线程。当已有创建的线程可用时,它也会重用它们。
3.3. 定时线程池
这个线程池在给定的延迟后或定期运行任务。
这是我们如何创建一个定时线程池:
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
在上面的代码片段中,整数参数是核心池大小。它表示即使线程处于空闲状态,也要保留在池中的线程数量。
3.4. 单线程池
这个池只包含一个线程。它按顺序执行提交的任务。如果发生异常并且线程被终止,将创建一个新的线程。
下面的代码片段显示了如何创建一个单线程池:
ExecutorService executorService = Executors.newSingleThreadExecutor();
在这里,_Executors_类的静态方法_newSingleThreadExecutor()创建了一个包含单个工作线程的_ExecutorService。
4. 线程与单线程执行器服务
我们可能会想知道,如果单线程池_ExecutorService_只包含一个线程,那么它与显式创建线程并使用它来执行任务有何不同。
现在让我们探索线程和只有一个工作线程的执行器服务之间的区别,以及何时使用哪一个。
4.1. 任务处理
**线程只能处理_Runnable_任务,而单线程执行器服务可以执行_Runnable_和_Callable_任务。**因此,我们也可以运行可以返回一些值的任务。
_ExecutorService_接口中的_submit()_方法接受一个_Callable_任务或_Runnable_任务,并返回一个_Future_对象。这个对象表示异步任务的结果。
另外,一个线程只能处理一个任务然后退出。但是,单线程执行器服务可以处理一系列任务,并将它们按顺序执行。
4.2. 线程创建开销
创建线程涉及到开销。例如,JVM需要分配内存。当在代码中反复创建线程时,它会影响性能。但是在单线程执行器服务的情况下,同一个工作线程被重用。因此,它防止了创建多个线程的开销。
4.3. 内存消耗
线程对象占用大量的内存。因此,如果我们为每个异步任务创建线程,可能会导致_OutOfMemoryError_。但是在单线程执行器服务中,同一个工作线程被重用,这导致内存消耗较少。
4.4. 资源释放
一个线程在其执行完成后释放资源。但是在执行器服务的情况下,我们需要关闭服务,否则JVM将无法关闭。像_shutdown()_和_shutdownNow()_这样的方法可以关闭执行器服务。
5. 结论
在本文中,我们学习了线程、执行器框架以及不同类型的线程池。我们还看到了线程和单线程执行器服务之间的区别。
我们了解到,如果有重复的工作或者有很多异步任务,那么执行器服务是更好的选择。
像往常一样,示例的源代码可以在GitHub上找到。