为什么这个 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;
用新的引用覆盖 Summation
的 sumValue
,而不是更新已经存在的引用的值。
如果您想从线程中获取 return 值,您需要使用 Callable
而不是 Runnable
。
但是,Callable
s 不能直接使用 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());
这是我的代码:
/* 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;
用新的引用覆盖 Summation
的 sumValue
,而不是更新已经存在的引用的值。
如果您想从线程中获取 return 值,您需要使用 Callable
而不是 Runnable
。
但是,Callable
s 不能直接使用 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());