【面试必会】线程池创建方式详解

2024-08-02 18:04:47 浏览数 (1)

最近面试问道了线程池的创建方式,这里出一篇文章记录下这一知识点!

线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的ThreadFactory创建一个新线程。

线程池的创建主要依赖于java.util.concurrent包下的ExecutorServiceExecutors类。最近面试问到了这块,所以这篇文章我们将详细介绍几种常见的线程池创建方式。

FixedThreadPool

FixedThreadPool是一种固定大小的线程池。它的核心线程数和最大线程数都是创建时指定的数值,且不会变化。就算空闲,也不会回收线程,除非设置了allowCoreThreadTimeOut

举个栗子:我们创建一个固定大小为5的线程池,并提交10个任务。由于线程池的大小固定为5,因此这5个线程会并发执行,而剩下的任务会等待前面的任务完成后再执行。

代码如下:

代码语言:java复制
	import java.util.concurrent.ExecutorService;  

	import java.util.concurrent.Executors;  

	  

	public class FixedThreadPoolDemo {  

	    public static void main(String[] args) {  

	        // 创建一个固定大小的线程池  

	        ExecutorService executorService = Executors.newFixedThreadPool(5);  

	  

	        for (int i = 0; i < 10; i  ) {  

	            Runnable worker = new WorkerThread(""   i);  

	            executorService.execute(worker);  // 执行任务  

	        }  

	        executorService.shutdown();  // 关闭线程池  

	        while (!executorService.isTerminated()) {  

	        }  

	        System.out.println("所有任务已完成");  

	    }  

	}  

	  

	class WorkerThread implements Runnable {  

	    private String command;  

	  

	    public WorkerThread(String s) {  

	        this.command = s;  

	    }  

	  

	    @Override  

	    public void run() {  

	        System.out.println(Thread.currentThread().getName()   " 开始. 命令 = "   command);  

	        processCommand();  

	        System.out.println(Thread.currentThread().getName()   " 结束.");  

	    }  

	  

	    private void processCommand() {  

	        try {  

	            Thread.sleep(5000);  

	        } catch (InterruptedException e) {  

	            e.printStackTrace();  

	        }  

	    }  

	}

CachedThreadPool

CachedThreadPool是一种缓存线程池,它的线程数量是不定的,可以说是几乎无限的。当提交一个任务时,如果线程池中有空闲线程,则立即执行;如果没有,则创建一个新线程执行。当线程空闲超过60秒,则自动回收。

举个栗子:

代码语言:java复制
	import java.util.concurrent.ExecutorService;  

	import java.util.concurrent.Executors;  

	  

	public class CachedThreadPoolDemo {  

	    public static void main(String[] args) {  

	        // 创建一个可缓存的线程池  

	        ExecutorService executorService = Executors.newCachedThreadPool();  

	  

	        for (int i = 0; i < 10; i  ) {  

	            Runnable worker = new WorkerThread(""   i);  

	            executorService.execute(worker);  // 执行任务  

	        }  

	        executorService.shutdown();  // 关闭线程池  

	        while (!executorService.isTerminated()) {  

	        }  

	        System.out.println("所有任务已完成");  

	    }  

	}

CachedThreadPool`适合执行大量的耗时较少的任务,如Web服务器。

SingleThreadExecutor

SingleThreadExecutor是一个单线程的Executor,它使用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。如果这个线程异常结束,会有另一个取代它,保证顺序执行。

代码语言:java复制
	import java.util.concurrent.ExecutorService;  

	import java.util.concurrent.Executors;  

	  

	public class SingleThreadExecutorDemo {  

	    public static void main(String[] args) {  

	        // 创建一个单线程的线程池  

	        ExecutorService executorService = Executors.newSingleThreadExecutor();  

	  

	        for (int i = 0; i < 10; i  ) {  

	            Runnable worker = new WorkerThread(""   i);  

	            executorService.execute(worker);  // 执行任务  

	        }  

	        executorService.shutdown();  // 关闭线程池  

	        while (!executorService.isTerminated()) {  

	        }  

	        System.out.println("所有任务已完成");  

	    }  

	}

SingleThreadExecutor中,由于只有一个线程,因此任务会按照提交的顺序一个接一个地执行,不会出现并发的情况

ScheduledThreadPool

ScheduledThreadPool 是 Java 并发包 java.util.concurrent 中提供的一个线程池实现,它用于在给定的延迟后运行命令,或者定期地执行命令。ScheduledThreadPool 的主要特点是能够处理需要定时执行或周期性执行的任务

举个栗子:

代码语言:java复制
	import java.util.concurrent.Executors;  

	import java.util.concurrent.ScheduledExecutorService;  

	import java.util.concurrent.TimeUnit;  

	  

	public class ScheduledThreadPoolDemo {  

	    public static void main(String[] args) {  

	        // 创建一个ScheduledThreadPool,包含3个核心线程  

	        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);  

	  

	        // 提交一个Runnable任务,在延迟2秒后执行  

	        scheduledExecutorService.schedule(new RunnableTask("一次性任务"), 2, TimeUnit.SECONDS);  

	  

	        // 提交一个Runnable任务,初始延迟2秒,之后每隔1秒执行一次  

	        scheduledExecutorService.scheduleAtFixedRate(new RunnableTask("周期性任务"), 2, 1, TimeUnit.SECONDS);  

	  

	        // 通常我们需要在程序结束时关闭线程池  

	        // scheduledExecutorService.shutdown();  

	    }  

	  

	    static class RunnableTask implements Runnable {  

	        private String command;  

	  

	        public RunnableTask(String command) {  

	            this.command = command;  

	        }  

	  

	        @Override  

	        public void run() {  

	            System.out.println(Thread.currentThread().getName()   " 开始执行命令: "   command   " at "   System.currentTimeMillis());  

	        }  

	    }  

	}

我们创建了一个 ScheduledThreadPool,它包含3个核心线程。然后,我们提交了两个任务:

  1. 一个是一次性任务,它在提交后的2秒后开始执行。
  2. 另一个是周期性任务,它在提交后的2秒开始执行,然后每隔1秒执行一次。

除了以上的用法

ScheduledExecutorService 提供了几个用于定时执行任务的方法:

  • schedule(Runnable command, long delay, TimeUnit unit):在给定延迟后运行命令一次。
  • scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit):初始延迟后开始首次执行,然后随后每隔固定周期执行一次。
  • scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit):初始延迟后开始首次执行,然后每次执行完毕后等待固定延迟再次执行。

如果任务执行过程中抛出异常,那么 ScheduledThreadPool 会停止该任务的后续执行,但不会停止线程池本身或其他任务的执行。如果需要处理任务执行中的异常,可以在任务内部进行捕获和处理。

以上就是创建线程池的几种方式,本篇文章到此结束,谢谢大家的观看!

0 人点赞