如何在 Java 中处理多个 API 请求?
微信搜索关注《Java学研大本营》,加入读者群,分享更多精彩
Java 中的并发是指语言并行运行多个线程的能力,允许同时执行多个任务。
线程池和Executor框架
一种方法是使用线程池来管理固定数量的线程,这些线程可以处理传入的请求。Java Executor 框架提供了一种方便的方法来实现这种方法,换句话说,使用线程池和 Executor 框架是在 Java 中实现并发和处理多个 API 请求的一种方法。
线程池管理固定数量的线程,可以有效利用系统资源,防止线程饥饿。
什么是执行器框架?
Executor 框架是一个内置的 Java 框架,它提供了一种管理和执行线程的方法。它是 java.util.concurrent 包的一部分,在 Java 5 中引入。
Executor 框架在低级 Thread 类上提供了更高级别的抽象,从而更容易并发执行任务,而无需直接管理线程。它还提供了一种方法来管理线程池并重用它们来执行多个任务,从而减少创建和销毁线程的开销。
Executor框架的核心接口是Executor接口,它定义了一个单一的方法execute(Runnable)。Executor 接口提供了一种提交 Runnable 任务以供执行的方法。该框架还提供了一些子接口和类,可以用来实现不同类型的Executor,例如:
-
ExecutorService:一个 Executor,它提供管理终止的方法和可以生成用于跟踪一个或多个异步任务进度的 Future 的方法。
-
ScheduledExecutorService:一个 ExecutorService,它可以安排命令在给定延迟后运行,或定期执行。
-
ThreadPoolExecutor:一个 ExecutorService,它使用可能的多个池线程之一执行每个提交的任务,通常使用 Executors 工厂方法配置。
使用 Executor 框架可以帮助您以高效的方式处理多个 API 请求,方法是管理线程并提供一种将它们重用于多个任务的方法,从而减少创建和销毁线程的开销。
下面是一个如何使用 Executor 框架高效处理多个 API 请求的示例:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExecutorExample {
public static void main(String[] args) {
// Create a fixed thread pool with 5 threads
ExecutorService executor = Executors.newFixedThreadPool(5);
// Submit 10 tasks for execution
for (int i = 0; i < 10; i++) {
final int taskId = i;
executor.submit(new Runnable() {
@Override
public void run() {
// Perform an API request here
// For example, using the OkHttp library:
// OkHttpClient client = new OkHttpClient();
// Request request = new Request.Builder()
// .url("https://example.com/api/task" + taskId)
// .build();
// Response response = client.newCall(request).execute();
// Do something with the response
System.out.println("Task " + taskId + " completed");
}
});
}
// Shut down the executor
executor.shutdown();
}
}
在本例中,我们使用Executors.newFixedThreadPool
方法创建一个具有 5 个线程的固定线程池。然后,我们使用该executor.submit
方法提交 10 个要执行的任务。每个任务代表一个 API 请求,该请求将由线程池中的线程之一执行。
提交所有任务后,shutdown()调用执行器的方法,启动线程池的关闭。池中的线程将执行完提交的任务,然后终止。
请注意,在此示例中,我添加了一个用于发出 API 请求的虚拟代码,它不起作用,您必须使用特定的库(如 OkHttp 或 Retrofit)来进行 API 调用。
您可以在这个示例中看到,通过重用固定数量的线程并通过 Executor 框架管理任务的执行,我们能够并发且高效地处理多个 API 请求。
异步 I/O 库
在 Java 中实现并发的另一种方法是通过使用异步 I/O (AIO) 库,它允许非阻塞 I/O 操作,并且可以处理大量并发连接。
异步 I/O 库是一个允许非阻塞 I/O 操作的库,这意味着程序可以在等待 I/O 操作完成的同时继续执行其他任务。这在处理大量并发连接时很有用,例如在构建高性能服务器时。
Java 中异步 I/O 库的一个例子是 Java NIO 包,自 Java 1.4 以来它是标准 Java 库的一部分。它提供了一组用于执行非阻塞 I/O 操作的类和接口。
下面是一个如何使用 Java NIO 包构建可以同时处理多个客户端的简单服务器的示例:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
public class NioServer {
public static void main(String[] args) throws IOException {
// Open a selector
Selector selector = Selector.open();
// Open a server socket channel
ServerSocketChannel serverSocket = ServerSocketChannel.open();
serverSocket.bind(new InetSocketAddress(8080));
serverSocket.configureBlocking(false);
// Register the server socket channel with the selector
serverSocket.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
// Wait for events
selector.select();
// Iterate over the events
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
// Handle a new connection
if (key.isAcceptable()) {
ServerSocketChannel channel = (ServerSocketChannel) key.channel();
SocketChannel client = channel.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
}
// Handle a read event
if (key.isReadable()) {
SocketChannel client = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int read = client.read(buffer);
if (read > 0) {
buffer.flip();
String message = new String(buffer.array(), 0, read);
System.out.println("Received: " + message);
}
}
iterator.remove();
}
}
}
}
在这个例子中,我们首先打开一个Selector对象和一个ServerSocketChannel. 被ServerSocketChannel配置为非阻塞的并注册到Selector. 然后我们进入一个无限循环,等待已注册频道上的事件。
当接受新连接时,我们SocketChannel为客户端创建一个新连接并将其注册到Selectorfor read 事件。当检测到读取事件时,我们将数据从客户端读取到缓冲区中并将其打印到控制台。
可以看到,通过使用 Java NIO 包,我们可以同时处理多个客户端,而不会阻塞程序的执行。Selector
以及ServerSocketChannel。
OkHttp 和 Retrofit
使用像 OkHttp 或 Retrofit 这样的库的概念也与并发密切相关,因为它抽象了底层网络和线程实现,使开发人员可以轻松高效地处理多个请求。
OkHttp
OkHttp 是一个流行的 Java 库,用于发出 HTTP 请求。它为执行同步和异步请求提供了一个简单高效的 API。它还包括连接池、透明 GZIP 压缩和响应缓存等功能。
下面是如何使用 OkHttp 在多线程环境中发出异步 GET 请求的示例。
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
public class OkHttpAsyncExample {
public static void main(String[] args) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url("https://jsonplaceholder.typicode.com/todos/1")
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (!response.isSuccessful()) {
throw new IOException("Unexpected code " + response);
}
String responseBody = response.body().string();
System.out.println(responseBody);
}
});
}
}
在这个例子中,我们首先创建一个实例,OkHttpClient
它是线程安全的,可以被多个请求共享。然后我们创建一个新Request
对象,指定我们要调用的 URL。
然后我们使用该enqueue
方法异步执行请求。该enqueue
方法采用一个Callback
对象,在请求完成或失败时调用该对象。在此示例中,我们在成功时打印响应主体,在失败时打印异常堆栈跟踪。
当你有多个传入的 API 请求时,你可以对所有请求使用同一个 OkHttpClient 实例,所有请求都将由 OkHttp 异步处理。该enqueue
方法立即返回,允许您的应用程序在后台获取响应的同时继续处理其他请求或任务。这有助于防止阻塞程序的执行并确保在处理大量并发连接时具有良好的性能。
改装
下面是如何使用 Retrofit 在多线程环境中发出异步 GET 请求的示例。
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class RetrofitAsyncExample {
public static void main(String[] args) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://jsonplaceholder.typicode.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
JsonPlaceholderApi jsonPlaceholderApi = retrofit.create(JsonPlaceholderApi.class);
Call<Todo> call = jsonPlaceholderApi.getTodo(1);
call.enqueue(new Callback<Todo>() {
@Override
public void onResponse(Call<Todo> call, Response<Todo> response) {
if (!response.isSuccessful()) {
System.out.println("Unexpected code " + response);
return;
}
Todo todo = response.body();
System.out.println(todo);
}
@Override
public void onFailure(Call<Todo> call, Throwable t) {
t.printStackTrace();
}
});
}
}
interface JsonPlaceholderApi {
@GET("todos/{id}")
Call<Todo> getTodo(@Path("id") int id);
}
class Todo {
int userId;
int id;
String title;
boolean completed;
// getters and setters
}
在此示例中,我们首先Retrofit
通过指定我们要调用的 API 的基本 URL 并添加一个 GSON 转换器工厂来创建一个实例。JsonPlaceholderApi
然后我们使用 Retrofit 的方法创建一个接口create
的实现来获取服务的实现。
然后我们调用getTodo
接口的方法,传递我们要检索的待办事项的 ID。这将返回一个Call
对象,我们可以使用该对象通过调用异步执行请求enqueue
。
该enqueue
方法采用一个Callback
对象,在请求完成或失败时调用该对象。在此示例中,我们在成功时打印响应主体,在失败时打印异常堆栈跟踪。
当你有多个传入的 API 请求时,你可以对所有请求使用相同的 Retrofit 实例,所有请求将由 Retrofit 异步处理。该enqueue
方法立即返回,允许您的应用程序在后台获取响应的同时继续处理其他请求或任务。这有助于防止阻塞程序的执行并确保在处理大量并发连接时具有良好的性能。
总之,以高效方式处理多个 API 请求的概念与 Java 中的并发性密切相关,可以通过使用线程池、异步 I/O (AIO) 库以及 OkHttp 或 Retrofit 等库来实现。
推荐书单
《项目驱动零起点学Java》
《项目驱动零起点学Java》共分 13 章,围绕 6 个项目和 258 个代码示例,分别介绍了走进Java 的世界、变量与数据类型、运算符、流程控制、方法、数组、面向对象、异常、常用类、集合、I/O流、多线程、网络编程相关内容。《项目驱动零起点学Java》总结了马士兵老师从事Java培训十余年来经受了市场检验的教研成果,通过6 个项目以及每章的示例和习题,可以帮助读者快速掌握Java 编程的语法以及算法实现。扫描每章提供的二维码可观看相应章节内容的视频讲解。
《项目驱动零起点学Java》贯穿6个完整项目,经过作者多年教学经验提炼而得,项目从小到大、从短到长,可以让读者在练习项目的过程中,快速掌握一系列知识点。
马士兵,马士兵教育创始人,毕业于清华大学,著名IT讲师,所讲课程广受欢迎,学生遍布全球大厂,擅长用简单的语言讲授复杂的问题,擅长项目驱动知识的综合学习。马士兵教育获得在线教育“名课堂”奖、“最受欢迎机构”奖。
赵珊珊,从事多年一线开发,曾为国税、地税税务系统工作。拥有7年一线教学经验,多年线上、线下教育的积累沉淀,培养学员数万名,讲解细致,脉络清晰。
精彩回顾
微信搜索关注《Java学研大本营》
访问【IT今日热榜】,发现每日技术热点