如果我能控制TaskManager
,我的第一个解决方案是更改它,使其在回调时也传递UUID参数或具有另一种控制方法 - 然后使用方法的结果将是无意义的。
然而,如果我没有这样的控制权,则可以采用另一种方法。
(编辑:我被告知在Java 8中处理此问题的正确方法是使用CompletableFuture - 请参见Stuarts的答案。)
另一种方法是使用“可变引用包装器”,例如Holder<T>
(或T[]
或自己创建),以模拟可变绑定。然后,
Holder<UUID> idRef = new Holder<UUID>();
idRef.value = TaskManager.newRepeatingTask(() -> {
for (int i = 0; i < profileButtons.length; i++) {
GuiButton button = profileButtons[i];
button.move(-1, 0);
}
toMove--;
if (toMove == 0) {
UUID id = idRef.value;
TaskManager.cancelTask(id);
}
}, 30L, 0L);
与使用带有UUID的Runnable方法相似,这种方法也存在潜在的竞态条件问题,即如果任务在不同的线程上运行,则ID的分配和潜在的lambda/Runnable内部使用之间可能存在竞争条件。(如果稍后在同一线程上运行,则同步问题不适用;如果立即在同一线程上运行,则lambda中永远不会观察到UUID。)
将共享同步应用于方法调用本身的外部/包装(以及适用代码周围的内部)应该可以解决这个问题,除非Runnable被立即调用。无论是否存在“竞态条件”,为了保证可见性,都应该进行同步或等效操作,如果采取这种方法并且任务可能在不同的线程上执行。
Holder<UUID> idRef = new Holder<UUID>();
synchronized(idRef) {
idRef.value = TaskManager.newRepeatingTask(() -> {
for (int i = 0; i < profileButtons.length; i++) {
GuiButton button = profileButtons[i];
button.move(-1, 0);
}
toMove--;
if (toMove == 0) {
synchronized(idRef) {
UUID id = idRef.value;
TaskManager.cancelTask(id);
}
}
}, 30L, 0L);
}
TaskManager
属于哪个包? - PM 77-1TaskManager
在我的工具类中,我完全掌控它。我不知道这些信息是为了什么而需要的。问题现在已经正式解决。 - CoderMusgrove