服务器程序(如数据库和 Web 服务器)重复执行来自多个客户端的请求,这些程序旨在处理大量短任务。 构建服务器应用程序的一种方法是在每次请求到达时创建一个新线程,并在新创建的线程中为这个新请求提供服务。 虽然这种方法实施起来似乎很简单,但它也有明显的缺点。 与处理实际请求相比,为每个请求创建新线程的服务器将花费更多时间和消耗更多系统资源来创建和销毁线程。
由于活动线程会消耗系统资源,同时创建过多线程的 JVM 会导致系统内存不足。 这就需要限制正在创建的线程数。
基于上述概念,自然而然我们就会考虑使用线程池。线程池重用先前创建的线程来执行当前任务,并为线程周期开销和资源抖动问题提供了解决方案。 由于请求到达时线程已经存在,因此消除了线程创建引入的延迟,使应用程序更具响应性。
Executor框架线程池的几种方法
Method Description
newFixedThreadPool(int) Creates a fixed size thread pool.
newCachedThreadPool() Creates a thread pool that creates new
threads as needed, but will reuse previously
constructed threads when they are available
newSingleThreadExecutor() Creates a single thread.
在固定线程池的情况下,如果执行器当前正在运行所有线程,则将挂起的任务放入队列中,并在线程空闲时执行。
在下文中,我们将看一个线程池执行器的基本示例——FixedThreadPool。
// Java program to illustrate
// ThreadPool
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// Task class to be executed (Step 1)
class Task implements Runnable
{
private String name;
public Task(String s)
{
name = s;
}
// Prints task name and sleeps for 1s
// This Whole process is repeated 5 times
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("Initialization Time for"
+ " task name - "+ name +" = " +ft.format(d));
//prints the initialization time for every task
}
else
{
Date d = new Date();
SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss");
System.out.println("Executing Time for task name - "+
name +" = " +ft.format(d));
// prints the execution time for every task
}
Thread.sleep(1000);
}
System.out.println(name+" complete");
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
public class Test
{
// Maximum number of threads in thread pool
static final int MAX_T = 3;
public static void main(String[] args)
{
// creates five tasks
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");
// creates a thread pool with MAX_T no. of
// threads as the fixed pool size(Step 2)
ExecutorService pool = Executors.newFixedThreadPool(MAX_T);
// passes the Task objects to the pool to execute (Step 3)
pool.execute(r1);
pool.execute(r2);
pool.execute(r3);
pool.execute(r4);
pool.execute(r5);
// pool shutdown ( Step 4)
pool.shutdown();
}
}
输出
Output:
Initialization Time for task name - task 2 = 02:32:56
Initialization Time for task name - task 1 = 02:32:56
Initialization Time for task name - task 3 = 02:32:56
Executing Time for task name - task 1 = 02:32:57
Executing Time for task name - task 2 = 02:32:57
Executing Time for task name - task 3 = 02:32:57
Executing Time for task name - task 1 = 02:32:58
Executing Time for task name - task 2 = 02:32:58
Executing Time for task name - task 3 = 02:32:58
Executing Time for task name - task 1 = 02:32:59
Executing Time for task name - task 2 = 02:32:59
Executing Time for task name - task 3 = 02:32:59
Executing Time for task name - task 1 = 02:33:00
Executing Time for task name - task 3 = 02:33:00
Executing Time for task name - task 2 = 02:33:00
Executing Time for task name - task 2 = 02:33:01
Executing Time for task name - task 1 = 02:33:01
Executing Time for task name - task 3 = 02:33:01
task 2 complete
task 1 complete
task 3 complete
Initialization Time for task name - task 5 = 02:33:02
Initialization Time for task name - task 4 = 02:33:02
Executing Time for task name - task 4 = 02:33:03
Executing Time for task name - task 5 = 02:33:03
Executing Time for task name - task 5 = 02:33:04
Executing Time for task name - task 4 = 02:33:04
Executing Time for task name - task 4 = 02:33:05
Executing Time for task name - task 5 = 02:33:05
Executing Time for task name - task 5 = 02:33:06
Executing Time for task name - task 4 = 02:33:06
Executing Time for task name - task 5 = 02:33:07
Executing Time for task name - task 4 = 02:33:07
task 5 complete
task 4 complete
从程序的执行中可以看出,只有当池中的线程空闲时,才会执行任务 4 或任务 5。 在那之前,额外的任务被放置在一个队列中。
线程池的最佳大小取决于可用处理器的数量和任务的性质。 在仅包含计算类型进程的队列的 N 处理器系统上,最大线程池大小为 N 或 N+1 将实现最大效率。 但是任务可能会等待 I/O,在这种情况下,我们会考虑请求的等待时间(W)和服务时间(S)的比率,同时最大池大小设置为 N*(1+ W/S) 以获得最大效率。
线程池是组织服务器应用程序的有用工具。 它在概念上非常简单,但是在实现和使用它时需要注意几个问题,例如死锁、资源抖动。
本文为从大数据到人工智能博主「xiaozhch5」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。