Thread与SingleThreadExecutorService

2023/06/07

1. 概述

线程和Executor框架是用于在Java中并行执行代码的两种机制,这提高了应用程序的性能。Executor框架提供了不同种类的线程池,其中一个池仅包含一个工作线程。

在本教程中,我们将了解线程和具有单个工作线程的ExecutorService之间的区别

2. 线程

线程是具有独立执行路径的轻量级进程,它用于并行执行任务。因此,可以有多个线程同时运行而不会相互干扰。

Thread对象执行Runnable任务。

让我们看看如何创建线程,我们可以通过扩展Thread类或实现Runnable接口来创建线程

让我们通过扩展Thread类来创建一个线程:

public class CustomThread extends Thread {
	// override the run() method to provide custom implementation

	public static void main(String[] args) {
		CustomThread t1 = new CustomThread();
		t1.start();
	}
}

在上面的示例中,CustomThread类扩展了Thread类。在main()方法中,我们创建了CustomThread类的对象,然后调用了它的start()方法,它开始执行线程。

下面我们来看一个实现Runnable接口创建线程的例子:

public class TestClass implements Runnable {
	// implement the run() method of Runnable interface

	public static void main(String[] args) {
		TestClass testClassRef = new TestClass();
		Thread t1 = new Thread(testClassRef);
		t1.start();
	}
}

在上面的示例中,TestClass实现了Runnable接口。我们在Thread类的构造函数中传递了TestClass对象的引用,然后,我们调用start()方法。这反过来又调用了TestClass实现的run()方法。

3. Executor框架

现在我们将学习Executor框架,它是在JDK 1.5中引入的。它是一个多线程框架,用于维护工作线程池并对其进行管理,任务在队列中提交,然后由这些工作线程执行。

它消除了在代码中显式创建线程的开销,相反,它会重用池中的线程来异步执行任务

现在让我们看看由Executor框架维护的不同种类的线程池

3.1 固定线程池

该池包含固定数量的线程,我们在创建池期间指定线程数。如果发生异常并且线程终止,则会创建一个新线程。

让我们看看如何创建一个固定的线程池:

ExecutorService executorService = Executors.newFixedThreadPool(5);

在上面的代码片段中,我们创建了一个具有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

我们可能想知道如果单线程池ExecutorService只包含一个线程,那么它与显式创建一个线程并使用它来执行任务有何不同。

现在让我们探讨Thread和只有一个工作线程的ExecutorService之间的区别,以及何时使用哪个。

4.1 任务处理

线程只能处理Runnable任务,而单线程ExecutorService可以同时执行Runnable和Callable任务。因此,使用它,我们也可以运行可以返回一些值的任务。

ExecutorService接口中的submit()方法接收Callable任务或Runnable任务并返回Future对象,该对象表示异步任务的结果。

此外,一个线程只能处理一个任务并退出,但是单线程ExecutorService可以处理一系列任务并按顺序执行它们。

4.2 线程创建开销

创建线程会产生开销,例如,JVM需要分配内存。当在代码中重复创建线程时,它会影响性能。但是在单线程ExecutorService的情况下,会重用同一个工作线程。因此,它可以防止创建多个线程的开销

4.3 内存消耗

线程对象占用大量内存,因此,如果我们为每个异步任务创建线程,就会导致OutOfMemoryError。但是在单线程ExecutorService中,可以重复使用同一个工作线程,从而减少内存消耗。

4.4 资源释放

线程在执行完成后释放资源,但是对于ExecutorService,我们需要关闭服务,否则JVM无法关闭。像shutdown()和shutdownNow()这样的方法可用于关闭ExecutorService

5. 总结

在本文中,我们了解了线程、Executor框架和不同类型的线程池,我们还看到了线程和单线程ExecutorService之间的差异。

我们了解到,如果有任何重复的作业或者有很多异步任务,那么ExecutorService是更好的选择。

与往常一样,本教程的完整源代码可在GitHub上获得。

Show Disqus Comments

Post Directory

扫码关注公众号:Taketoday
发送 290992
即可立即永久解锁本站全部文章