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