通过多线程计算PI的值
1.PI公式:
PI=4*(1-1/3+1/5-1/7+…1/(n*2-1))
2.多线程计算思路:
通过将PI的计算分为n个部分,分配到n个线程上进行计算,再将最后的结果进行求和。
3.计算步骤及原理:
- 设将PI的计算分为n个线程,共计算公式的前m次。
- 将一加一减固定为一个基本单位。即:
(1-1/3)+(1/5-1/7)+…+(1/(2n-3)+1/(n*2-1)), - 只要推导出每个线程执行的首项,末项和间隔即可。
-
计算首项:计算每个线程的首项分母,可以用数学归纳法计算,步骤如下:
1. 第1个线程,首项分母为1
2. 第2个线程,首项分母为5
3. 第3个线程,首项分母为9
所以可得出第n个线程,首先分母为4*n-3. -
计算末项:计算每个线程计算的最后一项。设项数为m,由PI的公式可以得知,最后一项为1/(2m-1),所以只要满足每个线程计算的分母小于或等于2m-1即可。
-
计算每个线程执行部分的间隔(具体执行哪些基本单位),因为分子都为1,所以我们只需要关注分母即可。推导过程如下(设分母间隔为step):
- 先假设只有1个线程,间隔单位就为0,所以当(1-1/3)执行完毕后,就该执行(1/5-1/7),所以1/3到1/5,step=2。
- 假设只有2个线程,间隔单位就为1,所以当线程1执行完(1-1/3)后,就该线程2执行(1/5-1/7),然后再由线程1执行 (1/9-1/11) 所以1/3到1/9,step=6。
- 同理,可得出当线程数n=3时,step=10;n=4时,setp=14.从而可以推导出step=4*n-2.
-
4.源代码:
package day26;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.*;
public class ComputePI {
public static void main(String[] args) {
ComputePI computePI = new ComputePI(1000000000, 100);
}
public ComputePI(int itemCount, int threadCount) {
//创建线程池对象
ExecutorService threadPool = Executors.newFixedThreadPool(threadCount);
List<Future<Double>> lstResult = new LinkedList<>();
for(int i = 1; i <= threadCount; i++){
PIComputer piComputer = new PIComputer(i*4-3, itemCount*2-1, threadCount*4-2 );
lstResult.add(threadPool.submit(piComputer));
}
double pi = 0.0;
while(lstResult.size()> 0){
for (int i = lstResult.size()-1; i>=0; i--) {
Future<Double> result = lstResult.get(i);
if (!result.isDone()) continue;
try {
pi += result.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
lstResult.remove(i);
}
}
pi *= 4;
System.out.println("PI="+pi);
threadPool.shutdown();
}
}
class PIComputer implements Callable<Double>{
// 最小项的分母
private int min;
// 最大项的分母
private int max;
// 每项步长
private int step;
/**
* 构造一个分量计算器
* @param min 最小项分母
* @param max 最大项分母
* @param step 分母步长(2的倍数)
*/
public PIComputer(int min, int max, int step) {
this.min = min;
this.max = max;
this.step = step;
}
@Override
public Double call() throws Exception {
int dived = min;
double sum = 0.0;
while (dived <= max){
sum += 1.0 / dived;
dived += 2;
sum -= 1.0 / dived;
// System.out.println("1/"+min+"-...-1/"+dived+"="+sum);
dived += step;
}
return sum;
}
}