面试题答案
一键面试冲突可能出现的情况
- 资源过早释放:当在
CompletableFuture
的get
方法阻塞期间,资源(如数据库连接)被提前释放。例如,连接池有一个超时机制来回收长时间未使用的连接,如果get
阻塞时间过长,连接可能被回收,而后续get
返回时使用该连接就会出现问题。 - 死锁:如果在获取资源和等待
CompletableFuture
结果之间存在循环依赖,可能会导致死锁。例如,线程A等待CompletableFuture
的结果,而该结果依赖于获取线程B持有的资源,同时线程B又在等待线程A释放它所占用的另一个资源。 - 资源泄漏:若在
get
方法阻塞过程中发生异常,但没有正确处理资源的关闭,资源(如网络连接)可能不会被正确释放,造成资源泄漏。
解决方案
- 设置合理的超时:使用
get(long timeout, TimeUnit unit)
方法来设置等待CompletableFuture
结果的超时时间,避免无限期阻塞。 - 异步资源管理:采用异步的方式管理资源,例如使用异步的数据库连接池(如HikariCP支持异步获取连接),在等待
CompletableFuture
结果的同时,不阻塞资源的获取和释放操作。 - 资源生命周期与
CompletableFuture
绑定:通过CompletableFuture
的回调机制,在任务完成(无论是正常完成还是异常完成)时,确保资源被正确释放。
代码层面实现
- 设置超时
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
// 异步操作,例如数据库查询
return "result";
});
try {
String result = future.get(5, TimeUnit.SECONDS);
System.out.println(result);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
e.printStackTrace();
}
- 异步资源管理(以数据库连接为例)
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.concurrent.CompletableFuture;
public class AsyncDatabaseAccess {
private static final HikariDataSource dataSource;
static {
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
config.setUsername("root");
config.setPassword("password");
dataSource = new HikariDataSource(config);
}
public static CompletableFuture<String> queryAsync() {
return CompletableFuture.supplyAsync(() -> {
try (Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement("SELECT column FROM table")) {
try (ResultSet resultSet = statement.executeQuery()) {
if (resultSet.next()) {
return resultSet.getString(1);
}
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
return null;
});
}
}
- 资源生命周期与
CompletableFuture
绑定
CompletableFuture<Connection> connectionFuture = CompletableFuture.supplyAsync(() -> {
try {
return dataSource.getConnection();
} catch (SQLException e) {
throw new RuntimeException(e);
}
});
connectionFuture.thenApplyAsync(connection -> {
try (PreparedStatement statement = connection.prepareStatement("SELECT column FROM table")) {
try (ResultSet resultSet = statement.executeQuery()) {
if (resultSet.next()) {
return resultSet.getString(1);
}
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
return null;
}).whenComplete((result, throwable) -> {
if (connectionFuture.isDone()) {
try {
connectionFuture.get().close();
} catch (InterruptedException | ExecutionException | SQLException e) {
e.printStackTrace();
}
}
});
通过以上方式,可以在代码层面实现对异步资源的高效管理,并避免因get
方法阻塞导致的资源泄漏或死锁问题。