Java多线程技术面试问得很多很多,而且在做脚本优化、接口异步调用、许多大型框架中使用非常广泛,提升性能巨大。不过实际工作中的业务开发,更多是为了保证业务的安全可靠和节省性能开销,都没怎么使用到。最近做了一个异步调用接口的需求,好久没用过了,一时还有点想不起来,还得看看java api文档,记录一下方便以后查看。
Java 异步的基本原理是利用多线程和回调函数。在异步操作中,主线程不必等待异步任务完成,而是可以继续执行其他任务。当异步任务完成时,通过回调函数或者其他机制通知主线程。
启动异步任务: 使用线程、CompletableFuture 或者其他异步编程工具启动异步任务,使其在新线程中执行。
主线程不阻塞: 主线程可以继续执行其他任务,而不必等待异步任务完成。
异步任务完成时通知主线程: 当异步任务完成时,通过回调函数、CompletableFuture 的 thenApply、thenAccept 等方法,或者其他方式通知主线程,以执行相应的逻辑。
处理异步任务的结果: 主线程在接收到异步任务完成的通知后,可以处理异步任务的结果。
// 示例:创建并启动一个线程
Thread myThread = new Thread(() -> {
// 异步任务逻辑
System.out.println("异步任务在新线程中执行");
});
myThread.start(); // 启动线程
// 示例:使用 Future
ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<String> futureResult = executorService.submit(() -> {
// 异步任务逻辑
return "异步任务的结果";
});
// 阻塞等待异步操作完成
String result = futureResult.get();
System.out.println("异步任务的结果:" + result);
CompletableFuture 是 Future 的一个扩展,提供更丰富的异步编程功能。它支持回调、组合多个异步操作等特性。
// 示例:使用 CompletableFuture
CompletableFuture<String> futureResult = CompletableFuture.supplyAsync(() -> {
// 异步任务逻辑
return "异步任务的结果";
});
// 注册回调函数
futureResult.thenAccept(result -> System.out.println("异步任务的结果:" + result));
CompletableFuture<String> futureResult = CompletableFuture.supplyAsync(() -> {
// 异步任务逻辑
return "异步任务的结果";
});
// 注册回调函数
futureResult.thenAccept(result -> System.out.println("异步任务的结果:" + result));
需求:需要分别调用A接口和B接口,两个返回结果之间没有关联。
public class AsyncExampleThread {
public static void main(String[] args) {
Thread threadA = new Thread(() -> {
String resultA = requestInterfaceA();
System.out.println("接口A的返回结果:" + resultA);
});
Thread threadB = new Thread(() -> {
String resultB = requestInterfaceB();
System.out.println("接口B的返回结果:" + resultB);
});
// 启动线程
threadA.start();
threadB.start();
// 主线程等待子线程执行完成
try {
threadA.join();
threadB.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static String requestInterfaceA() {
// 模拟接口A的请求
return "Response from Interface A";
}
private static String requestInterfaceB() {
// 模拟接口B的请求
return "Response from Interface B";
}
}
除了上述的方式,你也可以在主线程中调用A,在子线程中调用B,这样也无伤大雅,性能开销会更小一点,节省了一个线程。或者你可以考虑用线程池来做
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class AsyncExampleThreadPool {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2);
executorService.submit(() -> {
String resultA = requestInterfaceA();
System.out.println("接口A的返回结果:" + resultA);
});
executorService.submit(() -> {
String resultB = requestInterfaceB();
System.out.println("接口B的返回结果:" + resultB);
});
// 关闭线程池
executorService.shutdown();
}
private static String requestInterfaceA() {
// 模拟接口A的请求
return "Response from Interface A";
}
private static String requestInterfaceB() {
// 模拟接口B的请求
return "Response from Interface B";
}
}
CompletableFuture接口可能有些同学不太熟悉,举例一下它有哪些方法:
CompletableFuture<Integer> result = CompletableFuture.supplyAsync(() -> 10);
CompletableFuture<String> transformed = result.thenApply(value -> "Transformed result: " + value);
CompletableFuture<String> result = CompletableFuture.supplyAsync(() -> "Hello");
result.thenAccept(value -> System.out.println("Received result: " + value));
CompletableFuture<Integer> resultA = CompletableFuture.supplyAsync(() -> 5);
CompletableFuture<Integer> resultB = CompletableFuture.supplyAsync(() -> 10);
CompletableFuture<Integer> combined = resultA.thenCombine(resultB, (a, b) -> a + b);
CompletableFuture<String> result = CompletableFuture.supplyAsync(() -> {
throw new RuntimeException("Error!");
}).exceptionally(ex -> "Handled Exception: " + ex.getMessage());
CompletableFuture<String> result = CompletableFuture.supplyAsync(() -> "Hello");
CompletableFuture<String> handledResult = result.handle((res, ex) -> {
if (ex != null) {
return "Handled Exception: " + ex.getMessage();
} else {
return "Result: " + res;
}
});
CompletableFuture<Integer> result = CompletableFuture.supplyAsync(() -> 5);
CompletableFuture<Integer> composed = result.thenCompose(value -> CompletableFuture.supplyAsync(() -> value * 2));
CompletableFuture<String> resultA = CompletableFuture.supplyAsync(() -> "A");
CompletableFuture<String> resultB = CompletableFuture.supplyAsync(() -> "B");
// allOf 等待所有 CompletableFuture 完成
CompletableFuture<Void> allOfResult = CompletableFuture.allOf(resultA, resultB);
// anyOf 等待任一 CompletableFuture 完成
CompletableFuture<Object> anyOfResult = CompletableFuture.anyOf(resultA, resultB);
CompletableFuture<String> resultA = CompletableFuture.supplyAsync(() -> "A");
CompletableFuture<String> resultB = CompletableFuture.supplyAsync(() -> "B");
CompletableFuture<Void> runAfterBothResult = resultA.runAfterBoth(resultB, () ->
System.out.println("Both CompletableFuture completed")
);
CompletableFuture<String> resultA = CompletableFuture.supplyAsync(() -> "A");
CompletableFuture<String> resultB = CompletableFuture.supplyAsync(() -> "B");
CompletableFuture<Void> runAfterEitherResult = resultA.runAfterEither(resultB, () ->
System.out.println("Either CompletableFuture completed")
);
CompletableFuture<String> resultA = CompletableFuture.supplyAsync(() -> "A");
CompletableFuture<String> resultB = CompletableFuture.supplyAsync(() -> "B");
CompletableFuture<Void> thenAcceptBothResult = resultA.thenAcceptBoth(resultB, (a, b) ->
System.out.println("Result from A: " + a + ", Result from B: " + b)
);
代码示范一:组合两个接口返回的数据
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class AsyncExample {
public static void main(String[] args) {
// 异步请求接口A
CompletableFuture<String> resultA = CompletableFuture.supplyAsync(() -> requestInterfaceA());
// 异步请求接口B
CompletableFuture<String> resultB = CompletableFuture.supplyAsync(() -> requestInterfaceB());
// 使用thenAcceptBoth方法组合两个异步结果,处理它们的返回值
resultA.thenAcceptBoth(resultB, (responseA, responseB) -> {
System.out.println("接口A的返回结果:" + responseA);
System.out.println("接口B的返回结果:" + responseB);
// 在这里可以执行进一步的处理逻辑
});
// 主线程等待异步操作完成
try {
CompletableFuture.allOf(resultA, resultB).get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
private static String requestInterfaceA() {
// 模拟接口A的请求
// 返回接口A的结果
return "Response from Interface A";
}
private static String requestInterfaceB() {
// 模拟接口B的请求
// 返回接口B的结果
return "Response from Interface B";
}
}
代码示范二:分别处理返回结果
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class AsyncExample {
public static void main(String[] args) {
// 异步请求接口A
CompletableFuture<String> resultA = CompletableFuture.supplyAsync(() -> requestInterfaceA());
// 异步请求接口B
CompletableFuture<String> resultB = CompletableFuture.supplyAsync(() -> requestInterfaceB());
// 使用thenAccept方法在两个异步任务都完成时打印结果
resultA.thenAccept(responseA -> {
System.out.println("接口A的返回结果:" + responseA);
});
resultB.thenAccept(responseB -> {
System.out.println("接口B的返回结果:" + responseB);
});
// 主线程等待异步操作完成
try {
CompletableFuture.allOf(resultA, resultB).get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
private static String requestInterfaceA() {
// 模拟接口A的请求
// 返回接口A的结果
return "Response from Interface A";
}
private static String requestInterfaceB() {
// 模拟接口B的请求
// 返回接口B的结果
return "Response from Interface B";
}
}
这个有点整合方式一和方式二的味道,适用于对于需要更精细控制的项目,例如需要定制线程池的大小或其他行为。
import java.util.concurrent.*;
public class AsyncExampleExecutorService {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(2);
Future<String> resultA = executorService.submit(() -> requestInterfaceA());
Future<String> resultB = executorService.submit(() -> requestInterfaceB());
try {
String responseA = resultA.get();
String responseB = resultB.get();
System.out.println("接口A的返回结果:" + responseA);
System.out.println("接口B的返回结果:" + responseB);
// 在这里可以执行进一步的处理逻辑
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} finally {
executorService.shutdown();
}
}
private static String requestInterfaceA() {
// 模拟接口A的请求
return "Response from Interface A";
}
private static String requestInterfaceB() {
// 模拟接口B的请求
return "Response from Interface B";
}
}
使用异步技术在大多情况下能够提升性能,而且会很显著,但不是盲目地使用多线程就能提高性能了。