public final void sendAdvertisement(final Advertisement advertisment, int delay, final int repetitions){
final ScheduledFuture exec = executor.scheduleAtFixedRate( //<< initialized on this line
new Runnable(){
int totalSends = 0;
public void run(){
//do stuff here
if(++totalSends >= repetitions) exec.cancel(true); //<< here is says exec might not be initialized
}
},
0, delay, TimeUnit.MILLISECONDS);
}
如果无法做到这一点,您能建议更好的方法吗?我在ScheduledThreadPoolExecutor中找不到它的方法。基本上我要做的是让这段代码运行3次然后取消“计时器”。我可以使用Swing Timer,但我不想因为它用于其他东西。
它在评论中说,但这是错误:
%PATH%Discovery.java:148: variable exec might not have been initialized
if(++totalSends >= repetitions) exec.cancel(true);
答案 0 :(得分:2)
您的代码可能会从删除编译器警告的角度起作用,但警告的重点是指出您可能正在访问尚未分配的变量。即使exec
或exec[0]
为非null,也无法保证ScheduledFuture
对象甚至已正确初始化 - 即使内部线程可能正在运行,也是如此。这是非常危险的,可能会工作一段时间,但是当你转移到具有更多内核或不同负载环境的架构时,它会在生产中大幅失败。它也可以工作,但是从现在开始一个月后你就会更改do stuff here
代码并开始失败。
我看到了几种可以更好地完成此任务的方法。它们更复杂但也更安全并且与Java一致。首先想到的是使用AtomicReference
:
final AtomicReference<ScheduledFuture> futureReference =
new AtomicReference<ScheduledFuture>();
ScheduledFuture exec = executor.scheduleAtFixedRate(
new Runnable() {
int totalSends = 0;
public void run() {
//do stuff here
if (++totalSends >= repetitions) {
// we need to wait for the future to be initialized
while (futureReference.get() == null) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
Thread.currentThread.().interrupt();
}
}
futureReference.get().cancel(true);
}
}
},
0, delay, TimeUnit.MILLISECONDS);
futureReference.set(exec);
答案 1 :(得分:1)
我基本上有两种方法来解决这个问题。不幸的是,对于最终的代码质量,它们都不是特别好,所以我不确定我是否可以推荐它们。
第一个解决方案是使exec成为最终的单元素数组。然后你可以在声明后指定exec [0] =某事,即使数组本身是最终的。这种变化是使用/创建一些引用类(因为您可以更改最终引用的属性,但不能更改引用本身)。以下是一个简单的示例,但请记住,它不会考虑任何并发问题(请参阅下文):
final ScheduledFuture[] exec = new ScheduledFixture[1];
exec[0] = executor.scheduleAtFixedRate( //<< initialized on this line
new Runnable(){
int totalSends = 0;
public void run(){
//do stuff here
if(++totalSends >= repetitions) exec[0].cancel(true); //<< here is says exec might not be initialized
}
},
0, delay, TimeUnit.MILLISECONDS);
或者,您可以将exec移出方法的本地范围,并使其成为类属性。
但是,我必须警告你,特别是在初始延迟为零的情况下,在scheduleAtFixedRate方法返回之前,runnable中的代码有可能被执行,在这种情况下,exec [0]仍然为null。此外,您应该使用同步来确保主线程设置的exec [0]的值可用于负责执行runnable的线程。上述两种解决方案都应该有效,但我认为其中任何一种都不是特别好。
答案 2 :(得分:1)
为什么在知道执行次数时使用固定费率调度程序, 我认为简单的循环将完成这项工作
for (int i = 0; i < iterations; i++) {
executor.schedule(new Runnable() {
public void run() {
// do stuff here
}
}, delay * i, TimeUnit.MILLISECONDS);
}
正如WalterM所说:这不是创建许多新实例的好方法,请在循环中使用引用。