我试图在多个线程的帮助下总结从1到n的所有数字。
这是我的主题:
public class MyThread implements Runnable {
//Limits
int lowerLimit;
int upperLimit;
MyInteger result;
//Constructor
public MyThread(int lowerLimit, int upperLimit, MyInteger result){
this.lowerLimit = lowerLimit;
this.upperLimit = upperLimit;
this.result = result;
}
@Override
public void run() {
//Sums up the numbers in the given interval
for(int i= lowerLimit; i <= upperLimit;i++){
result.add(i);
}
}
}
它获得一个上限和一个下限,并在所有线程之间共享结果。
这是我的测试课:
public class Sum {
public static long sumThreads(int numberThreads, int n) {
Thread[] threads = new Thread[numberThreads];
MyInteger result = new MyInteger(0);
// Creates new threads and starts them
for (int i = 0; i < numberThreads; i++) {
threads[i] = new Thread(new MyThread(i * n / numberThreads + 1,
(i + 1) * n / numberThreads, result));
threads[i].start();
}
// Waits for all thread to continue with the return
for (int i = 0; i < threads.length; i++) {
try {
threads[i].join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return result.getValue();
}
public static void main(String[] args) {
System.out.println(sumThreads(10, 1000));
}
}
如果我仅使用一个线程,则结果是正确的。
如果我使用多个线程,则有时结果是正确的,并且大多数时候都太低了-这意味着线程尚未完成计算。但这怎么可能呢?我的for循环等待每个线程完成,因此当所有线程都计算出它们的部分时,该方法首先返回结果。
最好的祝福
编辑:
MyInteger类如下所示:
公共类MyInteger {
私人长期价值;
public MyInteger(int value) {
this.value = value;
}
public long getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public void add(int summand){
value += summand;
}
public void sub(int decr){
value -= decr;
}
public void increase(){
value++;
}
public void decrease(){
value--;
}
}
最佳答案
您的join
代码看起来正确。我怀疑您的代码遇到了race hazard
:
Wiki page on Race Condition
当有多个线程并发运行时,所有线程都在修改共享资源时,需要确保在执行修改时它们具有互斥访问权限。
为了快速确认这是一场数据竞赛,请尝试在synchronized
类的add()
方法中添加MyInteger
修饰符。
最终发生的是(对于两个线程的示例):
(假设MyInteger
实例的初始值为17。)
线程1调用add(3)
JVM(线程1)读取/查看MyInteger
具有值
17,并在添加操作期间单独维护
线程2调用add(5)
JVM(线程2)读取/查看MyInteger
的值为17,并在添加过程中单独维护该值
操作
JVM(线程1和线程2)分别增加
值,因此线程1的值为20,线程2的值为22。
JVM(线程1)将20写回MyInteger内部的原语
JVM(线程2)将22写入MyInteger内部的原语
现在,您已经丢失了add(3)
操作的结果,因为正确的原子操作会导致25
作为最终值。
除了线程安全的重要性外,最重要的是,即使是涉及原语的简单加法运算也不是作为单个原子操作执行的。而是将其分解为JVM内部的较小步骤,从而允许并发线程相互竞争。
这是Wiki文章中(优秀)表的屏幕截图,直观地展示了这一点:
关于java - 为什么join()现在在这里起作用?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/24982697/