Executor框架相比于传统的并发系统基础实现具有很多的优势。传统做法是实现一个Runnable接口的类,然后使用该类的对象来直接创建Thread实例。
这种做法有一些问题,特别是当你启动太多线程的时候,你可能降低了整个系统的性能。
Executor 框架里的基础组件
- Executor 接口:这是 executor 框架里的基本组件。它只定义了一个允许程序员给executor发送Runnable对象的方法。
- ExecutorService 接口:这个接口继承了 Executor 接口,并包含了额外一些方法来增加框架的功能,其中包括:
- 运行一些具有返回值的任务:Runnable 接口提供的 run() 方法没有返回值,但是使用 executors,你可以运行一些有返回值的任务
- 使用一个方法来执行一系列的任务
- 完成 executor 的执行并等待其销毁
- ThreadPoolExecutor 类:这个类实现了 Executor 和 ExecutorService 两个接口。另外,它提供了一些其它方法来获取 executor 的状态(工作线程数,已经被执行的任务数等等),配置 executor(最小和最大工作线程数)。
- Executors 类:该类提供了一些实用方法用以创建 Executor 对象以及其它相关的类。
本章提供了几个例子来解释Executor的使用。例子代码有点长,不过相对还是蛮简单的清晰的。
实例1 - K 邻近算法(K-nearest neighbors algorithm)
K邻近算法是一个用于监督分类的简单机器学习算法。算法包含以下主要部分:
- 训练数据集:该数据集由一系列样本组成,每个样本具由一个或多个属性,还有一个特殊属性来记录每个样本的标签
- 一个距离矩阵:这个矩阵用来计算需要分类的样本和训练数据集里样本的距离
- 测试数据集:该数据集用来衡量算法的行为。
当要对一个样本进行归类时,算法计算该样本和训练数据集里所有样本的距离。然后再取距离最小的的 k 个样本,这 k 个样本中,哪个标签数最多,那么这个标签就赋给要归类的那个样本。根据第一章得出的经验,我们从算法的串行版本开始,然后从串行版本演变到并行版本。
K邻近算法 - 串行版本
public class KnnClassifier {
private List<? extends Sample> dataSet;
private int k;
public KnnClassifier(List<? extends Sample> dataSet, int k) {
this.dataSet = dataSet;
this.k = k;
}
public String classify(Sample example) {
Distance[] distances = new Distance[dataSet.size()];
int index = 0;
// 计算新样本和训练数据集中各样本之间的距离
for (Sample localExample : dataSet) {
distances[index] = new Distance();
distances[index].setIndex(index);
distances[index].setDistance
(EuclideanDistanceCalculator.calculate(localExample,
example));
index++;
}
// 对计算得到的距离排序以便获取K个最近距离的样本
Arrays.sort(distances);
Map<String, Integer> results = new HashMap<>();
for (int i = 0; i < k; i++) {
Sample localExample = dataSet.get(distances[i].getIndex());
String tag = localExample.getTag();
results.merge(tag, 1, (a, b) -> a + b);
}
// 返回最近k个样本总数最多的那个标签
return Collections.max(results.entrySet(),
Map.Entry.comparingByValue()).getKey();
}
}
// 该类用来计算两个样本的距离
public class EuclideanDistanceCalculator {
public static double calculate (Sample example1, Sample
example2) {
double ret=0.0d;
double[] data1=example1.getExample();
double[] data2=example2.getExample();
if (data1.length!=data2.length) {
throw new IllegalArgumentException ("Vector doesn't have the
same length");
}
for (int i=0; i<data1.length; i++) {
ret+=Math.pow(data1[i]-data2[i], 2);
}
return Math.sqrt(ret);
}
}
K邻近算法 - 细颗粒度的并发版本
如果你分析以上的算法的并行版本,你会发现有两点你可以用并行来实现:
- 距离的计算:计算输入的样本和训练数据集中各样本的距离的循环中,每一个循环都是独立的,他们之间并不互相依赖。
- 距离的排序:Java 8 提供了 Arrays 类的 parallelSort() 方法来并发实现数据排序
在细颗粒度并发版本中,我们为每一个计算输入样本和训练数据集中样本的距离创建一个任务。由此可见,所谓的细颗粒度就是我们创建了很多的任务。
public class KnnClassifierParallelIndividual {
private List<? extends Sample> dataSet;
private int k;
private ThreadPoolExecutor executor;
private int numThreads;
private boolean parallelSort;
public KnnClassifierParallelIndividual(List<? extends Sample>
dataSet, int k,
int factor,
boolean parallelSort) {
this.dataSet = dataSet;
this.k = k;
// 动态获取运行此程序的处理器或核的数量来决定线程池中线程的数量
numThreads = factor * (Runtime.getRuntime().availableProcessors());
executor = (ThreadPoolExecutor)Executors.newFixedThreadPool(numThreads);
this.parallelSort = parallelSort;
}
/**
* 因为我们为每个距离计算创建了一个任务,因此主线程需要等待所有任务完成后才能继续,
* 我们使用 CountDownLatch 这个类来同步所有任务的完成,
* 我们用任务总数也就是数据集中样本的总数来初始化 CountDownLatch,
* 每个任务完成后调用 countDown() 方法
*/
public String classify(Sample example) throws Exception {
Distance[] distances = new Distance[dataSet.size()];
CountDownLatch endController = new CountDownLatch(dataSet.size());
int index = 0;
for (Sample localExample : dataSet) {
IndividualDistanceTask task = new
IndividualDistanceTask(distances, index, localExample,
example, endController);
executor.execute(task);
index++;
}
endController.await();
if (parallelSort) {
Arrays.parallelSort(distances);
} else {
Arrays.sort(distances);
}
Map<String, Integer> results = new HashMap<>();
for (int i = 0; i < k; i++) {
Sample localExample = dataSet.get(distances[i].getIndex());
String tag = localExample.getTag();
results.merge(tag, 1, (a, b) -> a + b);
}
// 返回最近k个样本总数最多的那个标签
return Collections.max(results.entrySet(),
Map.Entry.comparingByValue()).getKey();
}
public void destroy() {
executor.shutdown();
}
}
public class IndividualDistanceTask implements Runnable {
private Distance[] distances;
private int index;
private Sample localExample;
private Sample example;
private CountDownLatch endController;
public IndividualDistanceTask(Distance[] distances, int index,
Sample localExample,
Sample example, CountDownLatch endController) {
this.distances = distances;
this.index = index;
this.localExample = localExample;
this.example = example;
this.endController = endController;
}
public void run() {
distances[index] = new Distance();
distances[index].setIndex(index);
distances[index].setDistance
(EuclideanDistanceCalculator.calculate(localExample,
example));
// 任务完成,调用CountDownLatch的countDown()
endController.countDown();
}
}
K 邻近算法 - 粗颗粒度的并发算法版本
细颗粒度版本的问题是创建了太多的任务,粗颗粒度版本中,我们让每一个任务处理数据集的一个子集,这样避免创建太多的任务。
public class KnnClassifierParallelIndividual {
private List<? extends Sample> dataSet;
private int k;
private ThreadPoolExecutor executor;
private int numThreads;
private boolean parallelSort;
public KnnClassifierParallelIndividual(List<? extends Sample>
dataSet, int k, int factor, boolean parallelSort) {
this.dataSet = dataSet;
this.k = k;
// 动态获取运行此程序的处理器或核的数量来决定线程池中线程的数量
numThreads = factor * (Runtime.getRuntime().availableProcessors());
executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(numThreads);
this.parallelSort = parallelSort;
}
/**
* 因为我们为每个距离计算创建了一个任务,因此主线程需要等待所有任务完成后才能继续,
* 我们使用 CountDownLatch 这个类来同步所有任务的完成,
* 我们用任务总数也就是数据集中样本的总数来初始化 CountDownLatch,每个任务完成后调用 countDown() 方法
*/
public String classify(Sample example) throws Exception {
Distance distances[] = new Distance[dataSet.size()];
CountDownLatch endController = new CountDownLatch(numThreads);
int length = dataSet.size() / numThreads;
int startIndex = 0, endIndex = length;
for (int i = 0; i < numThreads; i++) {
GroupDistanceTask task = new GroupDistanceTask(distances,
startIndex, endIndex, dataSet, example, endController);
startIndex = endIndex;
if (i < numThreads - 2) {
endIndex = endIndex + length;
} else {
endIndex = dataSet.size();
}
executor.execute(task);
}
endController.await();
if (parallelSort) {
Arrays.parallelSort(distances);
} else {
Arrays.sort(distances);
}
Map<String, Integer> results = new HashMap<>();
for (int i = 0; i < k; i++) {
Sample localExample = dataSet.get(distances[i].getIndex());
String tag = localExample.getTag();
results.merge(tag, 1, (a, b) -> a + b);
}
// 返回最近k个样本总数最多的那个标签
return Collections.max(results.entrySet(),
Map.Entry.comparingByValue()).getKey();
}
public void destroy() {
executor.shutdown();
}
}
public class GroupDistanceTask implements Runnable {
private Distance[] distances;
private int startIndex, endIndex;
private Sample example;
private List<? extends Sample> dataSet;
private CountDownLatch endController;
public GroupDistanceTask(Distance[] distances, int startIndex,
int endIndex, List<? extends Sample> dataSet, Sample
example, CountDownLatch endController) {
this.distances = distances;
this.startIndex = startIndex;
this.endIndex = endIndex;
this.example = example;
this.dataSet = dataSet;
this.endController = endController;
}
public void run() {
for (int index = startIndex; index < endIndex; index++) {
Sample localExample = dataSet.get(index);
distances[index] = new Distance();
distances[index].setIndex(index);
distances[index].setDistance(EuclideanDistanceCalculator
.calculate(localExample, example));
}
endController.countDown();
}
}
实例2 - 并行在客户端/服务器端架构中的应用
在这个例子中:
- 客户端和服务器端通过sockets通信
- 客户端向服务器端发送查询以及其它请求,请求以字符串的形式发送,服务器端接收到请求后处理请求并返回处理结果
- 服务器端接受以下不同请求:
- Query:该类型请求格式为 q; codCountry; codIndicator; year
- Report: 该类型请求格式为 r; codIndicator
- Stop: 该请求格式为 z
- 其它请求类型,服务器返回错误信息
跟上述例子一样,我们先从串行版本入手然后过渡到并行版本。
客户端/服务器端 - 串行版本
程序包括以下三个主要部分:
- DAO部分,负责访问数据并且获得客户查询的结果 (我们在代码中忽略DAO的代码,因为它在本例中不是重点)
- 指令(command)部分,每一种请求类型对应相应的指令(command)类
- 服务器部分,它接收查询,调用相应的指令并返回查询结果给客户
以下是串行版本指令类部分的代码
//*****************串行版本指令类部分*****************//
// 指令的抽象类
public abstract class Command {
protected String[] command;
public Command (String [] command) {
this.command=command;
}
public abstract String execute ();
}
// 对应Query请求的指令类
public class QueryCommand extends Command {
public QueryCommand(String [] command) {
super(command);
}
public String execute() {
WDIDAO dao=WDIDAO.getDAO();
if (command.length==3) {
return dao.query(command[1], command[2]);
} else if (command.length==4) {
try {
return dao.query(command[1], command[2],
Short.parseShort(command[3]));
} catch (Exception e) {
return "ERROR;Bad Command";
}
} else {
return "ERROR;Bad Command";
}
}
}
//对应Report请求的指令类
public class ReportCommand extends Command {
public ReportCommand(String [] command) {
super(command);
}
public String execute() {
WDIDAO dao=WDIDAO.getDAO();
return dao.report(command[1]);
}
}
//对应Stop请求的指令类
public class StopCommand extends Command {
public StopCommand(String [] command) {
super(command);
}
public String execute() {
return "Server stopped";
}
}
//此类处理一些服务器不支持的请求
public class ErrorCommand extends Command {
public ErrorCommand(String [] command) {
super(command);
}
public String execute() {
return "Unknown command: "+command[0];
}
}
以下是服务器部分的代码
public class SerialServer {
public static void main(String[] args) throws IOException {
boolean stopServer = false;
System.out.println("Initialization completed.");
try (ServerSocket serverSocket = new ServerSocket(Constants.SERIAL_PORT)) {
// 不断循环,直到stopServer被设置为false
do {
try (Socket clientSocket = serverSocket.accept();
PrintWriter out = new PrintWriter
(clientSocket.getOutputStream(), true);
BufferedReader in = new BufferedReader(new
InputStreamReader(clientSocket.getInputStream()));) {
String line = in.readLine();
Command command;
String[] commandData = line.split(";");
System.out.println("Command: " + commandData[0]);
switch (commandData[0]) {
case "q":
System.out.println("Query");
command = new QueryCommand(commandData);
break;
case "r":
System.out.println("Report");
command = new ReportCommand(commandData);
break;
case "z":
System.out.println("Stop");
command = new StopCommand(commandData);
stopServer = true;
break;
default:
System.out.println("Error");
command = new ErrorCommand(commandData);
}
String response = command.execute();
System.out.println(response);
} catch (IOException e) {
e.printStackTrace();
}
} while (!stopServer);
} catch (Exception e) {
e.printStackTrace();
}
}
}
客户端/服务器端 - 并行版本
众所周知以上串行版本存在着严重的性能问题,服务器一次只能处理一个请求,其余的请求需要等待。并行版本中,我们将改为主线程接收请求,然后为每个请求创建一个任务,并交由线程池中的线程执行。
以下是并行版本指令类部分的代码,大部分代码和并行版本一样,除了Stop指令类。类名我们改为以"Concurrent"开始
//*****************串行版本指令类部分*****************//
// 指令的抽象类
public abstract class Command {
protected String[] command;
public Command (String [] command) {
this.command=command;
}
public abstract String execute ();
}
// 对应Query请求的指令类
public class ConcurrentQueryCommand extends Command {
public ConcurrentQueryCommand(String [] command) {
super(command);
}
public String execute() {
WDIDAO dao=WDIDAO.getDAO();
if (command.length==3) {
return dao.query(command[1], command[2]);
} else if (command.length==4) {
try {
return dao.query(command[1], command[2],
Short.parseShort(command[3]));
} catch (Exception e) {
return "ERROR;Bad Command";
}
} else {
return "ERROR;Bad Command";
}
}
}
//对应Report请求的指令类
public class ConcurrentReportCommand extends Command {
public ConcurrentReportCommand(String [] command) {
super(command);
}
public String execute() {
WDIDAO dao=WDIDAO.getDAO();
return dao.report(command[1]);
}
}
//对应Stop请求的指令类
public class ConcurrentStopCommand extends Command {
public ConcurrentStopCommand(String [] command) {
super(command);
}
public String execute() {
ConcurrentServer.shutdown();
return "Server stopped";
}
}
//此类处理一些服务器不支持的请求
public class ConcurrentErrorCommand extends Command {
public ConcurrentErrorCommand(String [] command) {
super(command);
}
public String execute() {
return "Unknown command: "+command[0];
}
}
//并行版本中新增了服务器状态查询指令
public class ConcurrentStatusCommand extends Command {
public ConcurrentStatusCommand (String[] command) {
super(command);
}
@Override
public String execute() {
StringBuilder sb=new StringBuilder();
ThreadPoolExecutor executor=ConcurrentServer.getExecutor();
sb.append("Server Status;");
sb.append("Actived Threads: ");
sb.append(String.valueOf(executor.getActiveCount()));
sb.append(";");
sb.append("Maximum Pool Size: ");
sb.append(String.valueOf(executor.getMaximumPoolSize()));
sb.append(";");
sb.append("Core Pool Size: ");
sb.append(String.valueOf(executor.getCorePoolSize()));
sb.append(";");
sb.append("Pool Size: ");
sb.append(String.valueOf(executor.getPoolSize()));
sb.append(";");
sb.append("Largest Pool Size: ");
sb.append(String.valueOf(executor.getLargestPoolSize()));
sb.append(";");
sb.append("Completed Task Count: ");
sb.append(String.valueOf(executor.getCompletedTaskCount()));
sb.append(";");
sb.append("Task Count: ");
sb.append(String.valueOf(executor.getTaskCount()));
sb.append(";");
sb.append("Queue Size: ");
sb.append(String.valueOf(executor.getQueue().size()));
sb.append(";");
return sb.toString();
}
}
以下是服务器部分代码和实现Runnable接口的RequestTask类
public class ConcurrentServer {
private static ThreadPoolExecutor executor;
private static ServerSocket serverSocket;
private static volatile boolean stopped = false;
public static void main(String[] args) throws InterruptedException {
serverSocket = null;
executor = (ThreadPoolExecutor) Executors.newFixedThreadPool
(Runtime.getRuntime().availableProcessors());
System.out.println("Initialization completed.");
serverSocket = new ServerSocket(Constants.CONCURRENT_PORT);
do {
try {
Socket clientSocket = serverSocket.accept();
RequestTask task = new RequestTask(clientSocket);
executor.execute(task);
} catch (IOException e) {
e.printStackTrace();
}
} while (!stopped);
executor.awaitTermination(1, TimeUnit.DAYS);
System.out.println("Shutting down cache");
System.out.println("Cache ok");
System.out.println("Main server thread ended");
}
public static void shutdown() {
stopped = true;
System.out.println("Shutting down the server...");
System.out.println("Shutting down executor");
executor.shutdown();
System.out.println("Executor ok");
System.out.println("Closing socket");
try {
serverSocket.close();
System.out.println("Socket ok");
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Shutting down logger");
System.out.println("Logger ok");
}
public static ThreadPoolExecutor getExecutor() {
return executor;
}
}
public class RequestTask implements Runnable {
private Socket clientSocket;
public RequestTask(Socket clientSocket) {
this.clientSocket = clientSocket;
}
public void run() {
try (PrintWriter out = new
PrintWriter(clientSocket.getOutputStream(),
true);
BufferedReader in = new BufferedReader(new
InputStreamReader(
clientSocket.getInputStream()));) {
String line = in.readLine();
Command command;
String[] commandData = line.split(";");
System.out.println("Command: " + commandData[0]);
switch (commandData[0]) {
case "q":
System.err.println("Query");
command = new ConcurrentQueryCommand(commandData);
break;
case "r":
System.err.println("Report");
command = new ConcurrentReportCommand(commandData);
break;
case "s":
System.err.println("Status");
command = new ConcurrentStatusCommand(commandData);
break;
case "z":
System.err.println("Stop");
command = new ConcurrentStopCommand(commandData);
break;
default:
System.err.println("Error");
command = new ConcurrentErrorCommand(commandData);
break;
}
ret = command.execute();
System.out.println(ret);
out.println(ret);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
其它有用的方法
Executors 类提供了另外一些方法创建 ThreadPoolExecutor 对象。这些方法包括:
- newCachedThreadPool():这个方法创建了一个 ThreadPoolExecutor 对象, 这种线程池能够重复利用那些空闲的工作线程,但是如果有需要,会创建新的工作线程。因此它没有最大的工作线程数。
- newSingleThreadExecutor():这个方法创建了一个只有一个工作线程的 ThreadPoolExecutor 对象。发送给executor的任务保存在队列中,一一被那个工作线程执行。
- CountDownLatch类提供了以下额外的方法: await(long timeout, TimeUnit unit) 当前线程等待直到内部计数器降为0;如果等待时间超过了参数 timeout,该方法返回 false。getCount() 该方法返回内部计数器的值。
Java中支持两种并行数据结构:
- 阻塞数据结构 (Blocking data structures):此类数据结构如果不能满足请求的操作,将阻塞访问的线程直到请求能被满足(例如取数据请求但数据结构中无数据)
- 非阻塞数据结构 (Non-blocking data structures):与阻塞数据结构不同,该数据结构无法满足请求时并不阻塞访问线程
有些数据结构实现了两种行为,有些数据结构则只实现一种行为。通常,阻塞数据结构同时也实现具有非阻塞行为的方法,但是非阻塞线程没有实现阻塞行为的方法。
阻塞操作的方法有:
- put(), putFirst(), putLast():插入数据到数据结构,如果数据结构已满,则阻塞访问线程直到数据结构有可用空间时
- take(), takeFirst(), takeLast():返回并删除数据结构中的数据。如果数据结构为空,则阻塞访问线程直到数据结构有一个元素
非阻塞操作的方法有:
- add(), addFirst(), addLast():插入数据到数据结构,如果数据结构已满,则抛出 IllegalStateException 异常
- remove(), removeFirst(), removeLast():返回并删除数据结构中的数据。如果数据结构为空,则抛出 IllegalStateException 异常
- element(), getFirst(), getLast():返回但不删除数据。如果数据结构为空,则抛出 IllegalStateException 异常
- offer(), offerFirst(), offerLast():插入数据到数据结构,如果数据结构已满,则返回 false
- poll(), pollFirst(), pollLast():返回并删除数据结构中的数据。如果数据结构为空,则返回 null
- peek(), peekFirst(), peekLast():返回但不删除数据。如果数据结构为空,则返回 null
分享到:
相关推荐
Java多线程实现数据切割批量执行,实现限流操作。 java线程池Executors实现数据批量操作。 批量异步Executors处理数据,实现限流操作,QPS限流。 线程池调用第三方接口限流实现逻辑。...4.多线程请求第三方接口限流。
3. **线程池(ExecutorService、ThreadPoolExecutor、Executors)**:Java提供了线程池来管理线程,当线程抛出异常时,线程池可以决定如何处理,比如是否需要关闭整个线程池,或者重新启动新的线程。 4. **异常传播...
在JAVA语言程序设计中,第十四章主要探讨的是多线程这一核心概念。多线程是Java编程中不可或缺的一部分,它允许程序同时执行多个独立的任务,从而提高应用程序的效率和响应性。在Java中,多线程是通过实现Runnable...
- **与Executor框架比较**:虽然`Callable`结合`FutureTask`可以独立使用,但在实际开发中更多地会结合`Executor`框架来使用,这样可以更好地管理线程资源,避免资源浪费。 #### 二、新增方式2:使用线程池 ##### ...
`Executor`框架提供了一种更灵活的方式来管理线程,例如`Executors.newCachedThreadPool()`创建一个可缓存的线程池,线程在空闲时会被缓存,下次需要时可复用,而`newFixedThreadPool(int nThreads)`则创建一个固定...
线程池管理和多线程上传是并发编程中的一个重要实践,特别是在大数据传输和网络服务中。在Java等编程语言中,线程池通过有效地管理和复用线程资源,避免了频繁创建和销毁线程带来的开销,提升了系统性能。下面将详细...
资源分类:Python库 所属语言:Python 资源全名:more-executors-1.19.1.tar.gz 资源来源:官方 安装方法:https://lanzao.blog.csdn.net/article/details/101784059
- 使用线程池管理线程资源。 #### 九、关于线程的讲解(出自Java原著) - **synchronized方法**: - `synchronized`修饰的方法确保线程同步访问。 - **synchronized关键字**: - 编译结果展示了`synchronized`的...
在Java编程领域,线程是并发处理的核心概念,它允许程序在同一时间执行多个任务。面试中,线程问题经常被用来测试候选人的并发处理能力和对Java内存模型的理解。以下是一些关于Java线程的常见面试知识点,这些内容...
总之,“more_executors-1.1.0-py2.py3-none-any.whl”是Python开发者的一个有力工具,它扩展了Python的标准并发处理库,为Python的并发编程提供了更多的选择和灵活性。无论你是Python新手还是经验丰富的开发者,这...
这个资源“more_executors-2.5.1-py2.py3-none-any.whl”正是从PyPI官网上获取的一个Python库,名为`more_executors`,版本号为2.5.1。 `more_executors`库是一个强大的扩展工具,它为Python的并发处理提供了更多的...
Java 5引入了Executor框架,它提供了一种更灵活的方式来管理线程。ExecutorService可以创建线程池,管理和控制线程的生命周期。Future接口代表异步计算的结果,可以检查计算是否完成,获取结果或取消任务。例如: ...
在Java编程中,多线程是一项关键特性,它允许程序同时执行多个独立的任务,从而提高程序的效率和响应性。本章重点讲解了多线程的相关概念和技术。 9.1 线程概念 线程被定义为程序中的单一顺序控制流。在多线程环境...
- **线程概念**:线程是程序执行的最小单元,每个进程可以有多个线程,它们共享同一内存空间,各自拥有独立的程序计数器、栈和局部变量。 - **多线程优点**:提高系统资源利用率,提高程序响应速度,实现并发执行...
这个名为"Android-Android多线程下载项目"的示例着重展示了如何利用线程池进行多线程管理和实现断点续传功能。在本文中,我们将深入探讨这些关键知识点。 1. **多线程下载** 在Android系统中,主线程(UI线程)...
- ExecutorService:Java并发包提供的线程池接口,用于管理线程生命周期,减少线程创建销毁的开销。 - ThreadPoolExecutor:ExecutorService的实现,可通过参数定制线程池的行为。 - Executors:提供静态工厂方法...
Java多线程是Java编程中的核心概念,它允许程序同时执行多个任务,提高了软件的效率和响应性。在Java中,多线程的实现主要有两种方式:通过继承Thread类和实现Runnable接口。这篇资料深入探讨了Java多线程的相关知识...
线程池可以有效地管理线程资源,避免频繁创建和销毁线程带来的开销。通过设置线程池的参数(核心线程数、最大线程数、线程存活时间等),我们可以控制并发程度和系统资源的利用率。 ### 3. 分页查询 在处理大量数据...