Java 多线程Pi计算器不一致答案

Java 多线程Pi计算器不一致答案,java,multithreading,Java,Multithreading,我试图创建一个简单的程序来计算PI,并自学多线程是如何工作的。每次我运行程序,我都会得到不同的PI答案。我假设这与每次以不同顺序运行的线程有关,但是在输出答案之前,我在每个线程上调用了Thread.join,因此我不确定计算的顺序为何会影响最终答案。我还尝试创建一个名为add的同步方法,以使Pi变量的更新线程安全,并将volatile修饰符添加到Pi变量中,但这并没有阻止问题的发生。我的代码都保存在一个类中,即 public class Pi implements Runnable{ s

我试图创建一个简单的程序来计算PI,并自学多线程是如何工作的。每次我运行程序,我都会得到不同的PI答案。我假设这与每次以不同顺序运行的线程有关,但是在输出答案之前,我在每个线程上调用了Thread.join,因此我不确定计算的顺序为何会影响最终答案。我还尝试创建一个名为add的同步方法,以使Pi变量的更新线程安全,并将volatile修饰符添加到Pi变量中,但这并没有阻止问题的发生。我的代码都保存在一个类中,即

public class Pi implements Runnable{
    static volatile double pi = 0;
    static long numRects = 100;
    static int rectsPerThread = 1   ;
    static double width = 1.0 / numRects;
    long start, end;

    public Pi(long start, long end){
        this.start = start;
        this.end = end;
    }

    @Override
    public void run(){
        double tmp = 0;
        for(long i = start; i < end; i++){
            tmp += Math.sqrt(1.0 - Math.pow(width * i, 2)) * width;
        }
        add(tmp);
    }

    private synchronized void add(double partOfPi){
        pi += partOfPi;
    }

    public static void main(String[] args){
        Thread[] threads = new Thread[(int)(numRects / rectsPerThread)];
        double start = System.nanoTime();
        for(int i  = 0; i < threads.length; i++){
            threads[i] = new Thread(new Pi(i * rectsPerThread, i * rectsPerThread + rectsPerThread));
            threads[i].start();
        }

        for(Thread t : threads){
            try{
                t.join();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }

        pi *= 4;
        System.out.println(pi);
        System.out.printf("Ran in: %.4fms", (System.nanoTime() - start) / Math.pow(10, 6));
    }
}
我的问题如下:我的多线程有什么错误,每次都会返回不同的答案,我该如何解决这个问题

var += expr;
这不是一个原子操作。旧值被读取、递增和回写,因此它不是线程安全的。你需要用信号灯来防止它

import java.util.concurrent.Semaphore;
public class Pi implements Runnable{
    static double pi = 0;
    static long numRects = 100;
    static int rectsPerThread = 1;
    static double width = 1.0 / numRects;
    long start, end;
    private static final Semaphore s = new Semaphore(1);

    public Pi(long start, long end){
        this.start = start;
        this.end = end; 
    }

    @Override
    public void run(){
        for(long i = start; i < end; i++){
            try{
                s.acquire();
            }catch (InterruptedException IE){ 
                return;
            }
            pi += Math.sqrt(1.0 - Math.pow(width * i, 2)) * width;
            s.release();
        }
    }

    public static void main(String[] args){
        Thread[] threads = new Thread[(int)(numRects / rectsPerThread)];
        double start = System.nanoTime();
        for(int i  = 0; i < threads.length; i++){
            threads[i] = new Thread(new Pi(i * rectsPerThread, i * rectsPerThread + rectsPerThread));
            threads[i].start();
        }

        for(Thread t : threads){
            try{
                t.join();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }

        pi *= 4;
        System.out.println(pi);
        System.out.printf("Ran in: %.4fms", (System.nanoTime() - start) / Math.pow(10, 6));
    }
}

pi值的读取和写入是线程安全的吗?为什么要这样做?Math.PI存在。@ScaryWombat这可能会解释问题所在,但即使在添加了一个同步方法以添加到PI后,同样的问题也会出现-为什么不使用此新代码编辑您的问题?@ScaryWombat听起来是个很好的主意:您对我为什么遇到此错误有任何反馈吗?这是有道理的。我已经用额外的代码更新了我的原始问题,使用了一个外部同步添加方法,以使添加线程安全。为什么使用信号量有效,而使用同步添加方法无效?同步方法在同一对象中有效;i、 e.它保证了Pi的同一实例中的互斥性。您每次都在创建一个新的Pi。我已将我的Pi变量设置为静态。除非我对静态变量的理解有很大的偏差,否则这意味着我只有一个pi实例。你有一个pi变量实例,是的,但是有很多pi类实例。每个同步的add函数保证没有两个线程可以同时执行同一个Pi对象的add函数。由于Pi对象实例是独立的,线程很高兴地对Pi静态变量执行冲突更新。注意大写的圆周率和小写的圆周率,谢谢!我对要同步的函数意味着什么的理解是错误的,因为无论出于何种原因,我假设它将在类的所有实例中同步,而不仅仅是在最初调用它的实例中。我修改了我的代码,这样在计算每个部分时,它不再向全局pi变量中添加,而是先计算所有部分,然后添加所有计算部分。再次感谢各位: