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

时间:2015-02-21 18:00:11

标签: java multithreading integer

这是我的代码:

/* 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);

    }

}

和Summation类:

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

为什么最后一行是&#39; null&#39; ?我期待再次看到120?

5 个答案:

答案 0 :(得分:4)

行。让我用图片解释一下。首先在main方法中创建一个引用并为其赋值null:

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

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

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

然后Summation.run()emthod为Summation.sumValue引用分配一个新的整数值:

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

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

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

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

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

然后将其传递给Summation:

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

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

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

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

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

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

答案 1 :(得分:2)

这是因为Integer是不可变的。

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

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

编辑:

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

您可以为sumValue创建一个访问者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;
    }
}

答案 2 :(得分:1)

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

sumValue = sum;

但是您要将字段设置为新的Integer实例。您没有将旧的Integer实例设置为新值。 这就是为什么主线程中的sumValue没有变化,除了null之外,它从未被设置为任何东西。

答案 3 :(得分:1)

在线程内部,您将SumValue分配给新的Object,因此在线程外部它仍为null。如果您计划多次运行此线程以获得多个总和,请尝试将总和存储在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);
}

答案 4 :(得分:1)

此代码

sumValue = sum;

使用新引用覆盖Summation的{​​{1}},而不是更新已存在的引用值。

如果您想从某个帖子返回值,则需要使用Callable而不是sumValue

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

最简单的方法是:

Callable

请注意,这会使用Future<Integer>,这是将来会返回的值。除非ExecutorService service = Executors.newSingleThreadExecutor(); Future<Integer> future = service.submit(new Summation()); Integer sumValue = future.get(); // The Future equivalent of join 完成处理,否则我们使用get阻止。

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