Java线程池示例
活动线程消耗系统资源,这可能导致JVM创建太多线程,这意味着系统将很快耗尽内存。
这就是Java中线程池帮助解决的问题。
线程池是如何工作的?
线程池为当前任务重用以前创建的线程。这解决了需要太多线程的问题,因此内存不足不是一个选择。你甚至可以把线程池看作是一个回收系统。它不仅消除了内存不足的选项,还使应用程序响应非常快,因为在请求到达时已经存在一个线程。
上图的工作流允许我们控制应用程序正在创建的线程数,还允许我们控制调度任务的执行,并将传入的任务保留在队列中。
执行者、执行者、执行者服务
Java提供了Executor框架,这意味着我们只需要实现可运行的对象并将它们发送给Executor来执行。
要使用线程池,首先我们需要创建ExecutorService对象并将任务传递给它。ThreadPoolExecutor类设置核心池大小和最大池大小。然后按顺序执行runnables。
不同的执行器线程池方法
newFixedThreadPool(int size) - creates a fixed size thread pool newCachedThreadPool() - creates a thread pool that creates new threads if needed but will also use previous threads if they are available newSingleThreadExecutor() - creates a single thread
ExecutorService接口包含许多用于控制任务进度和管理服务终止的方法。我们可以使用未来实例来控制任务的执行。如何使用Future的示例:
ExecutorService execService = Executors.newFixedThreadPool(6); Future<String> future = execService.submit(() -> "Example"); String result = future.get();
ThreadPoolExecutor允许我们实现一个可扩展的线程池,该线程池有许多参数,这些参数是*corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、handler、threadFactor。*但是,corePoolSize、maximumPoolSize和keepAliveTime是主要参数,因为它们在每个构造函数中都使用。
corePoolSize是池中要保留的线程数,即使它们处于空闲状态,除非设置了allowCoreThreadTimeOut。
maximumPoolSize是池中允许的最大线程数。
keepAliveTime是当线程数大于核心时,这是多余的空闲线程在终止前等待新任务的最长时间。
有关其他参数的详细信息,请访问原始的Oracle文档
线程池实现示例
工作流程步骤:
创建要执行的任务
使用执行器创建执行器池
将任务传递到执行器池
关闭执行器池
任务.java
import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; //(Step 1) public class Task implements Runnable { private String name; public Task(String name) { this.name = name; } public void run() { try { for (int i = 0; i < 5; i++) { if (i == 1) { Date date = new Date(); SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss"); System.out.println("Time initialization for task " + this.name + " is " + ft.format(date)); } else { Date date = new Date(); SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss"); System.out.println("Execution time for task " + this.name + " is " + ft.format(date)); } Thread.sleep(1000); } } catch(InterruptedException error) { error.printStackTrace(); } System.out.println(this.name + " completed"); } }
主类
import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Main { public static void main(String[] args) { Runnable task1 = new Task("task 1"); Runnable task2 = new Task("task 2"); Runnable task3 = new Task("task 3"); Runnable task4 = new Task("task 4"); Runnable task5 = new Task("task 5"); //(Step 2) ExecutorService pool = Executors.newFixedThreadPool(3); //(Step 3) pool.execute(task1); pool.execute(task2); pool.execute(task3); pool.execute(task4); pool.execute(task5); //(Step 4) pool.shutdown(); } }
输出
Time initialization for task task 2 is 10:18:40 Time initialization for task task 1 is 10:18:40 Time initialization for task task 3 is 10:18:40 Execution time for task task 3 is 10:18:41 Execution time for task task 1 is 10:18:41 Execution time for task task 2 is 10:18:41 Execution time for task task 2 is 10:18:42 Execution time for task task 3 is 10:18:42 Execution time for task task 1 is 10:18:42 Execution time for task task 1 is 10:18:43 Execution time for task task 3 is 10:18:43 Execution time for task task 2 is 10:18:43 Execution time for task task 3 is 10:18:44 Execution time for task task 1 is 10:18:44 Execution time for task task 2 is 10:18:44 task 2 completed task 1 completed task 3 completed Time initialization for task task 4 is 10:18:45 Time initialization for task task 5 is 10:18:45 Execution time for task task 4 is 10:18:46 Execution time for task task 5 is 10:18:46 Execution time for task task 4 is 10:18:47 Execution time for task task 5 is 10:18:47 Execution time for task task 5 is 10:18:48 Execution time for task task 4 is 10:18:48 Execution time for task task 4 is 10:18:49 Execution time for task task 5 is 10:18:49 task 4 completed task 5 completed
以上代码实现分解:
任务.java表示任务类。每个任务都有一个name实例变量,并且每个任务都是通过使用构造函数实例化的。这个类有一个方法,叫做run。在run方法体中,有一个for循环,该循环按任务数迭代。在我们的例子中,有5个任务,这意味着它将运行5次。第一次迭代,打印当前任务初始化的时间。其他迭代,打印执行时间。打印后,有一个线程睡眠()方法调用,用于以1秒延迟显示每个迭代消息。 注意方法名“run”的调用很重要,因为它是一个抽象方法,来自于我们的任务类正在实现的Runnable。
任务4和任务5只在池中的踏板空闲时执行。在此之前,另外的任务被放入队列中
执行完所有任务后,关闭线程池。
线程池什么时候有用
组织服务器应用程序时。正如本文开头所述,在组织服务器应用程序时,使用线程池非常有效,因为如果有许多任务,它会自动将它们放入队列中。不仅如此,它还可以防止内存耗尽,或者至少可以显著地减缓内存耗尽的过程。使用ExecutorService可以更容易地实现。