我的问题是:为什么在返回线程时设置非原始类型会起作用?
以下作品:
final int[] newTask = new int[1];
try{
Thread thread = new Thread(new Runnable(){
@Override
public void run(){
newTask[0] = someMethod();
return;
}
});
thread.start();
Thread.sleep(3000);
if(thread.isAlive()){
thread.interrupt();
newTask[0] = null;
}
}catch(InterruptedException ie){
log.error("Timeout", ie);
}
以下内容不是:
final int newTask;
try{
Thread thread = new Thread(new Runnable(){
@Override
public void run(){
newTask = someMethod();
return;
}
});
thread.start();
Thread.sleep(3000);
if(thread.isAlive()){
thread.interrupt();
newTask = null;
}
}catch(InterruptedException ie){
log.error("Timeout", ie);
}
非原语变量在从线程返回时起作用,但原语不起作用。为什么?
答案 0 :(得分:3)
在第一种情况下,newTask
是对数组的引用。无法更改引用,因为它是final
。可以修改数组的内容,因为数组不是不可变的。
在第二种情况下,newTask
是原始值。它无法更改,因为它是final
。
相同行为的一个更简单的例子是这样的:
final StringBuilder buf = new StringBuilder();
buf.append('x'); /* Modified the mutable object; no problem. */
buf = new StringBuilder(); /* Compiler error: you can't reassign final var */
它与线程无关,或者与原始值和引用类型之间存在差异。
答案 1 :(得分:1)
你正在混淆一些事情。它不是基元或非基元。在第一个变体中,您通过两个线程访问一个对象,在第二个变量中,您试图分配内部类的周围上下文的变量。
赋值不能正常工作,因为它会创建一个不确定的状态,因为对于周围的代码,变量可能被初始化或者可能是未初始化的。请注意,Java语言在这里更具限制性,因为它不关心您是否在多线程上下文中使用内部类。
不建议通过修改共享对象从线程返回值。有太多的方法可以犯错误,对于那些根本无法保证线程安全的数组尤其如此。
如果你必须手动处理Thread
,你仍然可以使用并发工具,这有助于避免线程错误:
Callable<Integer> task=new Callable<Integer>() {
public Integer call() throws Exception {
return someMethod();
}
};
FutureTask<Integer> f=new FutureTask<>(task);
new Thread(f).start();
try {
Integer i=f.get(3, TimeUnit.SECONDS);
// here we have a valid result
} catch (InterruptedException|ExecutionException ex) {
// log the failure
} catch (TimeoutException ex) {
f.cancel(true);
}
但通常情况下,您不是手动创建Thread
,而是使用ExecutorService
:
ExecutorService threadPool=Executors.newCachedThreadPool();
Callable<Integer> task=new Callable<Integer>() {
public Integer call() throws Exception {
return someMethod();
}
};
Future<Integer> f=threadPool.submit(task);
try {
Integer i=f.get(3, TimeUnit.SECONDS);
// here we have a valid result
} catch (InterruptedException|ExecutionException ex) {
// log the failure
} catch (TimeoutException ex) {
f.cancel(true);
}
threadPool.shutdown();
使用Java 8时,这变得更加清晰:
ExecutorService threadPool=Executors.newCachedThreadPool();
Future<Integer> f=threadPool.submit(() -> someMethod());
try {
Integer i=f.get(3, TimeUnit.SECONDS);
// here we have a valid result
} catch (InterruptedException|ExecutionException ex) {
// log the failure
} catch (TimeoutException ex) {
f.cancel(true);
}
threadPool.shutdown();
当然,ExecutorService
的重点在于您可以使用它来提交多个任务,因此您可能会在应用程序启动时创建一个ExecutorService
并调用shutdown()
在应用程序的生命周期结束时。