服务端应用程序(如数据库和 Web 服务器)需要处理来自客户端的高并发、耗时较短的请求任务,所以频繁的创建处理这些请求的所需要的线程就是一个非常消耗资源的操作。常规的方法是针对一个新的请求创建一个新线程,虽然这种方法似乎易于实现,但它有重大缺点。为每个请求创建新线程将花费更多的时间,在创建和销毁线程时花费更多的系统资源。因此同时创建太多线程的 JVM 可能会导致系统内存不足,这就需要限制要创建的线程数,也就是需要使用到线程池。 一、什么是 Java 中的线程池?
线程池技术就是线程的重用技术,使用之前创建好的线程来执行当前任务,并提供了针对线程周期开销和资源冲突问题的解决方案。 由于请求到达时线程已经存在,因此消除了线程创建过程导致的延迟,使应用程序得到更快的响应。
Java提供了以Executor接口及其子接口ExecutorService和ThreadPoolExecutor为中心的执行器框架。通过使用Executor,完成线程任务只需实现 Runnable接口并将其交给执行器执行即可。
为您封装好线程池,将您的编程任务侧重于具体任务的实现,而不是线程的实现机制。
若要使用线程池,我们首先创建一个 ExecutorService对象,然后向其传递一组任务。ThreadPoolExcutor 类则可以设置线程池初始化和最大的线程容量。
上图表示线程池初始化具有3 个线程,任务队列中有5 个待运行的任务对象。
执行器线程池方法
方法 描述newFixedThreadPool(int) 创建具有固定的线程数的线程池,int参数表示线程池内线程的数量
newCachedThreadPool() 创建一个可缓存线程池,该线程池可灵活回收空闲线程。若无空闲线程,则新建线程处理任务。
newSingleThreadExecutor() 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行
在固定线程池的情况下,如果执行器当前运行的所有线程,则挂起的任务将放在队列中,并在线程变为空闲时执行。
二、线程池示例在下面的内容中,我们将介绍线程池的executor执行器。
创建线程池处理任务要遵循的步骤
创建一个任务对象(实现Runnable接口),用于执行具体的任务逻辑
使用Executors创建线程池ExecutorService
将待执行的任务对象交给ExecutorService进行任务处理
停掉 Executor 线程池
//第一步: 创建一个任务对象(实现Runnable接口),用于执行具体的任务逻辑 (Step 1) class Task implements Runnable { private String name; public Task(String s) { name = s; } // 打印任务名称并Sleep 1秒 // 整个处理流程执行5次 public void run() { try{ for (int i = 0; i<=5; i++) { if (i==0) { Date d = new Date(); SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss"); System.out.println("任务初始化" + name +" = " + ft.format(d)); //第一次执行的时候,打印每一个任务的名称及初始化的时间 } else{ Date d = new Date(); SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss"); System.out.println("任务正在执行" + name +" = " + ft.format(d)); // 打印每一个任务处理的执行时间 } Thread.sleep(1000); } System.out.println("任务执行完成" + name); } catch(InterruptedException e) { e.printStackTrace(); } } }测试用例
public class ThreadPoolTest { // 线程池里面最大线程数量 static final int MAX_SIZE = 3; public static void main (String[] args) { // 创建5个任务 Runnable r1 = new Task("task 1"); Runnable r2 = new Task("task 2"); Runnable r3 = new Task("task 3"); Runnable r4 = new Task("task 4"); Runnable r5 = new Task("task 5"); // 第二步:创建一个固定线程数量的线程池,线程数为MAX_SIZE ExecutorService pool = Executors.newFixedThreadPool(MAX_SIZE); // 第三步:将待执行的任务对象交给ExecutorService进行任务处理 pool.execute(r1); pool.execute(r2); pool.execute(r3); pool.execute(r4); pool.execute(r5); // 第四步:关闭线程池 pool.shutdown(); } }示例执行结果
任务初始化task 1 = 05:25:55 任务初始化task 2 = 05:25:55 任务初始化task 3 = 05:25:55 任务正在执行task 3 = 05:25:56 任务正在执行task 1 = 05:25:56 任务正在执行task 2 = 05:25:56 任务正在执行task 1 = 05:25:57 任务正在执行task 3 = 05:25:57 任务正在执行task 2 = 05:25:57 任务正在执行task 3 = 05:25:58 任务正在执行task 1 = 05:25:58 任务正在执行task 2 = 05:25:58 任务正在执行task 2 = 05:25:59 任务正在执行task 3 = 05:25:59 任务正在执行task 1 = 05:25:59 任务正在执行task 1 = 05:26:00 任务正在执行task 2 = 05:26:00 任务正在执行task 3 = 05:26:00 任务执行完成task 3 任务执行完成task 2 任务执行完成task 1 任务初始化task 5 = 05:26:01 任务初始化task 4 = 05:26:01 任务正在执行task 4 = 05:26:02 任务正在执行task 5 = 05:26:02 任务正在执行task 4 = 05:26:03 任务正在执行task 5 = 05:26:03 任务正在执行task 5 = 05:26:04 任务正在执行task 4 = 05:26:04 任务正在执行task 4 = 05:26:05 任务正在执行task 5 = 05:26:05 任务正在执行task 4 = 05:26:06 任务正在执行task 5 = 05:26:06 任务执行完成task 4 任务执行完成task 5