为什么这个 Java 程序没有显示我期望的结果?

Why is this Java Program not showing me the result I expect?

这是我的代码:

/* User: koray@tugay.biz Date: 21/02/15 Time: 19:53 */

public class DriverClass {

    public static void main(String[] args) throws InterruptedException {

        int upper = 15;
        Integer sumValue = null;
        Thread thread = new Thread(new Summation(upper, sumValue));
        thread.start();
        thread.join();
        System.out.println(sumValue);

    }

}

和求和class:

class Summation implements Runnable {

    private int upper;
    private Integer sumValue;

    public Summation(int upper, Integer sumValue) {
        this.upper = upper;
        this.sumValue = sumValue;
    }

    public void run() {
        System.out.println("Thread started...");
        int sum = 0;
        for (int i = 0; i <= upper; i++) {
            sum += i;
        }
        System.out.println("Sum is:" + sum);
        sumValue = sum;
    }

}

我会在控制台看到:

Thread started...
Sum is:120
null

为什么最后一行是 'null'?我期待再次看到 120?

Summation 的构造函数中,您设置了字段 sumValue。 然后在函数 run 中再次设置字段 sumValue

sumValue = sum;

但是您正在将字段设置为新的 Integer 实例。您没有将旧的 Integer 实例设置为新值。 这就是为什么主线程中的 sumValue 没有改变,它从未被设置为 null.

以外的任何东西

在线程内您将 SumValue 分配给一个新对象,因此在线程外它仍然为空。如果您计划 运行 此线程多次获得多个总和,请尝试将总和存储在 Map 中,其中键是上面的数字,值是总和。

所以:

      Map<Integer,Integer> sumMap = new TreeMap<Integer,Integer>();
      Thread thread = new Thread(new Summation(upper, sumMap));


public Summation(int upper, Map<Integer,Integer> sumMap) {
    this.upper = upper;
    this.sumMap= sumMap;
}

public void run() {
    System.out.println("Thread started...");
    int sum = 0;
    for (int i = 0; i <= upper; i++) {
        sum += i;
    }
    System.out.println("Sum is:" + sum);
    sumMap.put(upper,sum);
}

发生这种情况是因为 Integer 是不可变的。

您正在通过构造函数将 sumValue 传递给 Summation,但在构造函数中创建了一个新的 Integer 来复制您传递给它的值。

为了更好地理解这里发生的事情,您可能想看看 this article 请记住,您正在使用不可变对象(您的 Integer 对象)

编辑:

创建自己的 class 扩展整数?看看这个 immutable class should be final?

您可以改为为您的 sumValue 创建访问器 a getter:

public class DriverClass {

    public static void main(String[] args) throws InterruptedException {
        int upper = 15;
        Integer sumValue = null;
        Summation sum = new Summation(upper, sumValue);

        Thread thread = new Thread(sum);
        thread.start();
        thread.join();

        sumValue = sum.getSumValue();

        System.out.println(sumValue);
    }

}

class Summation implements Runnable {

    private final int upper;
    private Integer sumValue;

    public Summation(int upper, Integer sumValue) {
        this.upper = upper;
        this.sumValue = sumValue;
    }

    @Override
    public void run() {
        System.out.println("Thread started...");
        int sum = 0;
        for (int i = 0; i <= upper; i++) {
            sum += i;
        }
        System.out.println("Sum is:" + sum);
        sumValue = sum;
    }

    public Integer getSumValue() {
        return sumValue;
    }
}

这段代码

sumValue = sum;

用新的引用覆盖 SummationsumValue,而不是更新已经存在的引用的值。

如果您想从线程中获取 return 值,您需要使用 Callable 而不是 Runnable

但是,Callables 不能直接使用 Thread。相反,您必须使用 ExecutorService.

最简单的方法是:

ExecutorService service = Executors.newSingleThreadExecutor();
Future<Integer> future = service.submit(new Summation());
Integer sumValue = future.get(); // The Future equivalent of join

请注意,这使用了 Future<Integer>, which is a value that will be returned in the future. We use get,它会阻塞,除非 Future 完成处理。

请注意,您必须重新设计求和以实现 Callable<Integer> 而不是 Runnable,并将 public void run() 更改为 public Integer call()return sum;(或 sumValue)。

好的。我用一张图解释一下。首先,您在 main 方法中创建一个引用并将 null 分配给它:

main
----
sumValue -----> null

然后将此引用传递给 Summation 构造函数。这将创建引用的副本:

main
----
sumValue -----> null
                ^
Summation       |
----            |
sumValue --------

然后 Summation.run() 方法将一个新的整数值分配给 Summation.sumValue 引用:

main
----
sumValue -----> null

Summation       
----            
sumValue -----> 120

如你所见,main方法中的引用仍然指向null。这就是打印 null 的原因。

相反,您可以做的是传递对可变对象的引用,该对象的状态可以改变(例如 AtomicInteger):

main
----
sumValue -----> AtomicInteger(0)

然后传给求和:

main
----
sumValue -----> AtomicInteger(0)
                ^
Summation       |
----            |
sumValue --------

然后在Summation中,修改AtomicInteger的状态:sumValue.set(120):

main
----
sumValue -----> AtomicInteger(120)
                ^
Summation       |
----            |
sumValue --------

然后,当在 main 方法中打印 AtomicInteger 时,您将得到 120,因为 main 方法和 Summation 对象有两个指向同一对象的引用。

或者更好的是,您可以将结果作为 Integer 存储在 Summation 对象中,并在计算完成后在 main 方法中请求结果:

System.out.println(summation.getSumValue());