从线程返回时设置非基本类型

时间:2015-09-30 16:55:54

标签: java multithreading

我的问题是:为什么在返回线程时设置非原始类型会起作用?

以下作品:

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);
    }

非原语变量在从线程返回时起作用,但原语不起作用。为什么?

2 个答案:

答案 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()在应用程序的生命周期结束时。