【发布时间】:2018-12-15 02:45:25
【问题描述】:
我需要从CompletableFuture<Void> 返回主线程。
我有一个CompletableFuture 的网络,它运行异步以处理接收并插入到 SQL 数据库中的数据。然而,一旦所有这些都完成了,我想返回主线程并执行一个函数,我没有找到合理的方法。
调用join() 或get() 只会阻塞CompletableFuture 正在运行的线程...我也不想连续轮询isDone()。
我尝试使用whenComplete(),但它似乎仍然无法按预期工作......
public static void updatePermIconCache() {
Logger.debug("thread1: " + Thread.currentThread().getId());
hubDatabase.getLobbies().thenAccept(s -> {
for (String lobby : s) {
for (int i = 0; i < 27; i++) {
final int x = i;
hubDatabase.isInPermTable(lobby, i).thenAccept(b -> {
if (!b) return;
hubDatabase.cachePermToJoinTable(lobby, x);
});
}
}
}).whenComplete((v, t) -> registerPanels());
}
我希望我的 registerPanels() 函数在主线程中被调用,但考虑到即使函数 updatePermIconCache() 被称为异步,以及确定是否调用此函数的其他函数,我不明白如何,好吧,让它回到主线程。
编辑: 把代码改成这个...
public static void updatePermIconCache() {
Logger.debug("thread1: " + Thread.currentThread().getId());
hubDatabase.getLobbies().thenAccept(s -> {
for (String lobby : s) {
for (int i = 0; i < 27; i++) {
final int x = i;
hubDatabase.isInPermTable(lobby, i).thenAccept(b -> {
if (!b) return;
hubDatabase.cachePermToJoinTable(lobby, x).join();
registerPanels();
});
}
}
});
}
问题是...主线程 id 是21,在函数registerPanels() 的第一行我打印了Thread#currentThread#getId(),它打印了多个不同的数字,范围在30 和42 之间。如果它在主线程上运行,它应该打印21,对吗?
编辑: 这是第一个被调用的异步运行方法
public CompletableFuture<Void> calibrate() {
return CompletableFuture.runAsync(() -> {
getLobbies().thenAccept(a -> Arrays.stream(a).forEach(this::createPermToJoinTable));
getLobbies().thenAccept(a -> Arrays.stream(a).forEach(this::createVanishedTable));
});
}
public CompletableFuture<String[]> getLobbies() {
String sql = "SELECT * FROM lobby";
List<String> list = new ArrayList<>();
return queryAsync(r -> {
while(r.next()) {
list.add(r.getString(1));
}
return r;
}, sql).thenApplyAsync(v -> list.toArray(new String[list.size()]));
}
这些方法最初是从这里调用的...
public static CompletableFuture<Void> addToPerm(String lobby) {
HubDatabase hubDatabase = MinelightHub.getHubDatabase();
return hubDatabase.calibrate().thenRunAsync(() -> {
hubDatabase.addToPermToJoinTable(lobby, 0, "Admin", getDesc(true), Material.WOOL, getData(DyeColor.RED), Permissions.ADMIN, true);
hubDatabase.addToPermToJoinTable(lobby, 1, "Mod", getDesc(false), Material.WOOL, getData(DyeColor.ORANGE), Permissions.MOD, false);
hubDatabase.addToPermToJoinTable(lobby, 2, "Builder", getDesc(false), Material.WOOL, getData(DyeColor.CYAN), Permissions.BUILDER, false);
hubDatabase.addToPermToJoinTable(lobby, 3, "YouTube", getDesc(false), Material.WOOL, getData(DyeColor.RED), Permissions.YOUTUBE, false);
hubDatabase.addToPermToJoinTable(lobby, 9, "JrAdmin", getDesc(false), Material.WOOL, getData(DyeColor.RED), Permissions.JRADMIN, false);
hubDatabase.addToPermToJoinTable(lobby, 10, "JrMod", getDesc(false), Material.WOOL, getData(DyeColor.ORANGE), Permissions.JRMOD, false);
hubDatabase.addToPermToJoinTable(lobby, 11, "Artist", getDesc(false), Material.WOOL, getData(DyeColor.CYAN), Permissions.ARTIST, false);
hubDatabase.addToPermToJoinTable(lobby, 12, "Twitch", getDesc(false), Material.WOOL, getData(DyeColor.PURPLE), Permissions.TWITCH, false);
hubDatabase.addToPermToJoinTable(lobby, 17, "Default", getDesc(false), Material.WOOL, getData(DyeColor.GRAY), Permissions.DEFAULT, false);
hubDatabase.addToPermToJoinTable(lobby, 18, "SrMod", getDesc(false), Material.WOOL, getData(DyeColor.ORANGE), Permissions.SRMOD, false);
hubDatabase.addToPermToJoinTable(lobby, 19, "Trial", getDesc(false), Material.WOOL, getData(DyeColor.ORANGE), Permissions.TRIAL, false);
hubDatabase.addToPermToJoinTable(lobby, 20, "Partner", getDesc(false), Material.WOOL, getData(DyeColor.BLUE), Permissions.PARTNER, false);
hubDatabase.addToPermToJoinTable(lobby, 21, "VIP", getDesc(false), Material.WOOL, getData(DyeColor.GREEN), Permissions.VIP, false);
}).thenRunAsync(InventoryManager::updatePermIconCache);
}
现在您可以看到有助于了解这种情况的必要路径。
当我创建一个“大厅”时,#addToPerm(String lobby) 方法开始运行
不过,最初,需要先创建 SQL 数据库中的表,然后才能更改向其中添加任何数据......这就是(在#addToPerm(String lobby) 方法中)#calibrate 方法所做的。一旦它创建了大厅,我需要用数据填充它,例如hubDatabase#addToPermJoinTable()。添加完所有数据后,我需要更新本地缓存InventoryManager#updatePermIconCache()。加载完所有缓存后,我需要返回主线程并调用 #registerPanels() 方法,因为该方法使用线程不安全 API。我仍然坚持如何做到这一点......
【问题讨论】:
-
这期间主线程在做些什么吗?还是您的意思是 GUI 线程从中进行 GUI 调用?在后一种情况下:您使用的是哪个 GUI(Swing、JavaFX)?
-
在主线程和
CompletableFuture线程中调用registerPanels有什么区别?当数据库插入完成时,registerPanels会在您当前的设置中被调用吗? -
join()将阻塞主线程直到 completablefuture 完成 -
主线程是什么意思? main() 方法在哪里运行?这是一个普通线程,只是第一个线程。
-
为什么要在“主”线程上运行
registerPanels()?它有什么特别之处?好的解决方案需要这些信息与您相关,否则可能只是纯粹的猜测。提供minimal reproducible example 也有助于更好地理解问题。
标签: java multithreading minecraft bukkit completable-future