Java虚拟机并发编程中运算结果不确定问题
@duanjigang @send_linux@hbsycw @redcap0 @zavakid @gnah
Java虚拟机编程技术大家谈---多核和并发编程
我在这个活动中获得了《Java虚拟机并发编程 》图书1本.看了一下,照着书上的例子作了一下,发现了一个问题.
在书第18页的那个关于线程池的例子,我在原例子上略作了一些改动,并没有影响到核心问题,但是每次运行的结果都是不确定的
以下是我的测试代码
这个类是从服务器获取数据文件,例子中的网址是雅虎财经的,我现在打开那个连接已经无效了,因此使用本地的Nginx提供文件package com.fangzhaoguo.finance;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
public class YahooFinance {
final static int ID = 0;
final static int Name = 1;
private URL url;
private BufferedReader reader;
public YahooFinance(final String host) {
try {
url = new URL(host);
reader = new BufferedReader(new InputStreamReader(url.openStream()));
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public double getPrice(final String ticker, int way) {
String data = null;
String[] dataItem;
double lastPrice = 0;
try {
while (null != (data = reader.readLine())) {
dataItem = data.split(",");
if (ticker.equals(dataItem)) {
lastPrice = Double.valueOf(dataItem);
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
return lastPrice;
}
public void close() {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
这个类就是自己建立线程池来处理并发的请求,以前不是并发的时候完全没有问题,改成并发之后,每次运行的结果都不一样package com.fangzhaoguo.finance;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
public class YahooFinanceNAV {
String host, filename;
YahooFinance finance;
Map<String, Integer> stocks;
public YahooFinanceNAV(String host, String filename) {
this.host = host;
this.filename = filename;
finance = new YahooFinance(host);
stocks = new HashMap<String, Integer>();
}
public void readTickers() {
try {
final BufferedReader reader = new BufferedReader(new FileReader(
filename));
String info = null;
String[] infoItem;
while (null != (info = reader.readLine())) {
infoItem = info.split(",");
stocks.put(infoItem, Integer.valueOf(infoItem));
}
reader.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private int getPoolSize() {
return 4 + stocks.size() / Runtime.getRuntime().availableProcessors();
}
public double computeNAV() {
double NAV = 0.0;
int poolSize = getPoolSize();
List<Callable<Double>> partitions = new ArrayList<Callable<Double>>();
for (final String ticker : stocks.keySet()) {
partitions.add(new Callable<Double>() {
public Double call() throws Exception {
return stocks.get(ticker)
* finance.getPrice(ticker, YahooFinance.ID);
}
});
}
ExecutorService executorService = Executors
.newFixedThreadPool(poolSize);
try {
List<Future<Double>> valFutures = executorService.invokeAll(
partitions, 10000, TimeUnit.SECONDS);
for (final Future<Double> vaFuture : valFutures) {
NAV += vaFuture.get();
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
executorService.shutdown();
finance.close();
return NAV;
}
}
package com.fangzhaoguo.finance;
public class Main {
public static void main(String[] args) {
YahooFinanceNAV yahooFinanceNAV = new YahooFinanceNAV(
"http://localhost/table.csv", "stocks.dat");
yahooFinanceNAV.readTickers();
System.out.println(yahooFinanceNAV.computeNAV());
}
} 看看这样行不行?
for (final Future<Double> vaFuture : valFutures) {
if(vaFuture.isDone()) {
NAV += vaFuture.get();
}
} craaazy123 发表于 2013-07-18 11:53 static/image/common/back.gif
看看这样行不行?
for (final Future vaFuture : valFutures) {
if(vaFuture.isDone()) {
额,谢谢你 这不是线程安全的问题,而是线程执行顺序不确定 这样的场景,推荐使用 CompletionService ,然后用 take 来获得 future,这样可以最快拿到结果。 craaazy123 发表于 2013-07-18 11:53 static/image/common/back.gif
看看这样行不行?
for (final Future vaFuture : valFutures) {
if(vaFuture.isDone()) {
我试了不可以
但是在另外一个例子中同样的方法是成功的.感觉这个问题是由于IO速度慢造成的,不过我是本机测试都这样,如果换成是直接从网上获取数据那不更慢 gnah 发表于 2013-07-18 12:58 static/image/common/back.gif
这不是线程安全的问题,而是线程执行顺序不确定
最后的数据结果不一致,应该是由数据丢失了,我做了一个没有磁盘IO的例子,不存在这个问题
我试一下用虚拟磁盘吧
这是用虚拟盘软件制作了一个虚拟磁盘,盘符是F:
将所有的文件都放在虚拟盘中,运算结果还是不一致
试一下楼上建议的CompletionService吧
感觉这个问题很怪异,我试了另外一个例子完全没有问题,是个算素数的,没有磁盘IO操作,是运算密集型的例子 回复 5# zavakid public double computeNAV() {
double NAV = 0.0;
int poolSize = getPoolSize();
ExecutorService executorService = Executors
.newFixedThreadPool(poolSize);
CompletionService<Double> completionService = new ExecutorCompletionService<Double>(
executorService);
for (final String ticker : stocks.keySet()) {
completionService.submit(new Callable<Double>() {
public Double call() throws Exception {
return stocks.get(ticker)
* finance.getPrice(ticker, YahooFinance.ID);
}
});
}
for (int i = 0; i < stocks.size(); i++) {
try {
NAV += completionService.take().get();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
executorService.shutdown();
finance.close();
return NAV;
}我从网上看了一个例子,是这么个用法,可是运行起来还是老毛病,结果总是老变
页:
[1]
2