免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 4052 | 回复: 17
打印 上一主题 下一主题

Java虚拟机并发编程中运算结果不确定问题 [复制链接]

论坛徽章:
19
CU大牛徽章
日期:2013-03-13 15:32:35CU大牛徽章
日期:2013-09-18 15:15:15CU大牛徽章
日期:2013-05-20 10:46:44CU大牛徽章
日期:2013-05-20 10:46:38CU大牛徽章
日期:2013-05-20 10:46:31CU大牛徽章
日期:2013-05-20 10:46:25CU大牛徽章
日期:2013-05-20 10:46:18CU大牛徽章
日期:2013-04-17 11:19:51CU大牛徽章
日期:2013-04-17 11:19:42CU大牛徽章
日期:2013-04-17 11:19:37CU大牛徽章
日期:2013-04-17 11:19:32CU大牛徽章
日期:2013-04-17 11:19:28
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-07-18 10:56 |只看该作者 |倒序浏览
@duanjigang @send_linux
@hbsycw @redcap0 @zavakid @gnah


Java虚拟机编程技术大家谈---多核和并发编程
我在这个活动中获得了《Java虚拟机并发编程 》图书1本.看了一下,照着书上的例子作了一下,发现了一个问题.

在书第18页的那个关于线程池的例子,我在原例子上略作了一些改动,并没有影响到核心问题,但是每次运行的结果都是不确定的

以下是我的测试代码

这个类是从服务器获取数据文件,例子中的网址是雅虎财经的,我现在打开那个连接已经无效了,因此使用本地的Nginx提供文件
  1. package com.fangzhaoguo.finance;

  2. import java.io.BufferedReader;
  3. import java.io.IOException;
  4. import java.io.InputStreamReader;
  5. import java.net.MalformedURLException;
  6. import java.net.URL;

  7. public class YahooFinance {

  8.         final static int ID = 0;
  9.         final static int Name = 1;

  10.         private URL url;
  11.         private BufferedReader reader;

  12.         public YahooFinance(final String host) {
  13.                 try {
  14.                         url = new URL(host);
  15.                         reader = new BufferedReader(new InputStreamReader(url.openStream()));
  16.                 } catch (MalformedURLException e) {
  17.                         e.printStackTrace();
  18.                 } catch (IOException e) {
  19.                         e.printStackTrace();
  20.                 }
  21.         }

  22.         public double getPrice(final String ticker, int way) {
  23.                 String data = null;
  24.                 String[] dataItem;
  25.                 double lastPrice = 0;

  26.                 try {
  27.                         while (null != (data = reader.readLine())) {
  28.                                 dataItem = data.split(",");
  29.                                 if (ticker.equals(dataItem[way])) {
  30.                                         lastPrice = Double.valueOf(dataItem[dataItem.length - 1]);
  31.                                         break;
  32.                                 }
  33.                         }
  34.                 } catch (IOException e) {
  35.                         e.printStackTrace();
  36.                 }
  37.                 return lastPrice;
  38.         }

  39.         public void close() {
  40.                 try {
  41.                         reader.close();
  42.                 } catch (IOException e) {
  43.                         e.printStackTrace();
  44.                 }
  45.         }
  46. }
复制代码
这个类就是自己建立线程池来处理并发的请求,以前不是并发的时候完全没有问题,改成并发之后,每次运行的结果都不一样
  1. package com.fangzhaoguo.finance;

  2. import java.io.BufferedReader;
  3. import java.io.FileNotFoundException;
  4. import java.io.FileReader;
  5. import java.io.IOException;
  6. import java.util.ArrayList;
  7. import java.util.HashMap;
  8. import java.util.List;
  9. import java.util.Map;
  10. import java.util.concurrent.Callable;
  11. import java.util.concurrent.ExecutionException;
  12. import java.util.concurrent.ExecutorService;
  13. import java.util.concurrent.Executors;
  14. import java.util.concurrent.Future;
  15. import java.util.concurrent.TimeUnit;

  16. public class YahooFinanceNAV {

  17.         String host, filename;
  18.         YahooFinance finance;
  19.         Map<String, Integer> stocks;

  20.         public YahooFinanceNAV(String host, String filename) {
  21.                 this.host = host;
  22.                 this.filename = filename;
  23.                 finance = new YahooFinance(host);
  24.                 stocks = new HashMap<String, Integer>();
  25.         }

  26.         public void readTickers() {
  27.                 try {
  28.                         final BufferedReader reader = new BufferedReader(new FileReader(
  29.                                         filename));
  30.                         String info = null;
  31.                         String[] infoItem;
  32.                         while (null != (info = reader.readLine())) {
  33.                                 infoItem = info.split(",");
  34.                                 stocks.put(infoItem[0], Integer.valueOf(infoItem[1]));
  35.                         }
  36.                         reader.close();
  37.                 } catch (FileNotFoundException e) {
  38.                         e.printStackTrace();
  39.                 } catch (IOException e) {
  40.                         e.printStackTrace();
  41.                 }
  42.         }

  43.         private int getPoolSize() {
  44.                 return 4 + stocks.size() / Runtime.getRuntime().availableProcessors();
  45.         }

  46.         public double computeNAV() {
  47.                 double NAV = 0.0;
  48.                 int poolSize = getPoolSize();
  49.                 List<Callable<Double>> partitions = new ArrayList<Callable<Double>>();
  50.                 for (final String ticker : stocks.keySet()) {
  51.                         partitions.add(new Callable<Double>() {
  52.                                 public Double call() throws Exception {
  53.                                         return stocks.get(ticker)
  54.                                                         * finance.getPrice(ticker, YahooFinance.ID);
  55.                                 }
  56.                         });
  57.                 }

  58.                 ExecutorService executorService = Executors
  59.                                 .newFixedThreadPool(poolSize);
  60.                 try {
  61.                         List<Future<Double>> valFutures = executorService.invokeAll(
  62.                                         partitions, 10000, TimeUnit.SECONDS);
  63.                         for (final Future<Double> vaFuture : valFutures) {
  64.                                 NAV += vaFuture.get();
  65.                         }
  66.                 } catch (InterruptedException e) {
  67.                         e.printStackTrace();
  68.                 } catch (ExecutionException e) {
  69.                         e.printStackTrace();
  70.                 }
  71.                 executorService.shutdown();
  72.                 finance.close();
  73.                 return NAV;
  74.         }
  75. }
复制代码
  1. package com.fangzhaoguo.finance;

  2. public class Main {

  3.         public static void main(String[] args) {
  4.                 YahooFinanceNAV yahooFinanceNAV = new YahooFinanceNAV(
  5.                                 "http://localhost/table.csv", "stocks.dat");
  6.                 yahooFinanceNAV.readTickers();
  7.                 System.out.println(yahooFinanceNAV.computeNAV());
  8.         }
  9. }
复制代码

论坛徽章:
6
CU大牛徽章
日期:2013-05-20 10:43:41IT运维版块每日发帖之星
日期:2016-07-29 06:20:00IT运维版块每日发帖之星
日期:2016-01-27 06:20:00CU大牛徽章
日期:2013-05-20 10:44:16CU大牛徽章
日期:2013-05-20 10:44:0615-16赛季CBA联赛之广东
日期:2018-03-09 11:17:08
2 [报告]
发表于 2013-07-18 11:53 |只看该作者
看看这样行不行?  
for (final Future<Double> vaFuture : valFutures) {
      if(vaFuture.isDone()) {
            NAV += vaFuture.get();
       }
}

论坛徽章:
19
CU大牛徽章
日期:2013-03-13 15:32:35CU大牛徽章
日期:2013-09-18 15:15:15CU大牛徽章
日期:2013-05-20 10:46:44CU大牛徽章
日期:2013-05-20 10:46:38CU大牛徽章
日期:2013-05-20 10:46:31CU大牛徽章
日期:2013-05-20 10:46:25CU大牛徽章
日期:2013-05-20 10:46:18CU大牛徽章
日期:2013-04-17 11:19:51CU大牛徽章
日期:2013-04-17 11:19:42CU大牛徽章
日期:2013-04-17 11:19:37CU大牛徽章
日期:2013-04-17 11:19:32CU大牛徽章
日期:2013-04-17 11:19:28
3 [报告]
发表于 2013-07-18 12:01 |只看该作者
craaazy123 发表于 2013-07-18 11:53
看看这样行不行?  
for (final Future vaFuture : valFutures) {
      if(vaFuture.isDone()) {


额,谢谢你

论坛徽章:
4
CU十二周年纪念徽章
日期:2013-10-24 15:41:34狮子座
日期:2014-02-10 17:22:57技术图书徽章
日期:2014-02-18 08:44:12技术图书徽章
日期:2014-04-29 14:15:42
4 [报告]
发表于 2013-07-18 12:58 |只看该作者
这不是线程安全的问题,而是线程执行顺序不确定

论坛徽章:
0
5 [报告]
发表于 2013-07-18 14:57 |只看该作者
这样的场景,推荐使用 CompletionService ,然后用 take 来获得 future,这样可以最快拿到结果。

论坛徽章:
19
CU大牛徽章
日期:2013-03-13 15:32:35CU大牛徽章
日期:2013-09-18 15:15:15CU大牛徽章
日期:2013-05-20 10:46:44CU大牛徽章
日期:2013-05-20 10:46:38CU大牛徽章
日期:2013-05-20 10:46:31CU大牛徽章
日期:2013-05-20 10:46:25CU大牛徽章
日期:2013-05-20 10:46:18CU大牛徽章
日期:2013-04-17 11:19:51CU大牛徽章
日期:2013-04-17 11:19:42CU大牛徽章
日期:2013-04-17 11:19:37CU大牛徽章
日期:2013-04-17 11:19:32CU大牛徽章
日期:2013-04-17 11:19:28
6 [报告]
发表于 2013-07-18 22:55 |只看该作者
craaazy123 发表于 2013-07-18 11:53
看看这样行不行?  
for (final Future vaFuture : valFutures) {
      if(vaFuture.isDone()) {


我试了不可以

但是在另外一个例子中同样的方法是成功的.感觉这个问题是由于IO速度慢造成的,不过我是本机测试都这样,如果换成是直接从网上获取数据那不更慢

论坛徽章:
19
CU大牛徽章
日期:2013-03-13 15:32:35CU大牛徽章
日期:2013-09-18 15:15:15CU大牛徽章
日期:2013-05-20 10:46:44CU大牛徽章
日期:2013-05-20 10:46:38CU大牛徽章
日期:2013-05-20 10:46:31CU大牛徽章
日期:2013-05-20 10:46:25CU大牛徽章
日期:2013-05-20 10:46:18CU大牛徽章
日期:2013-04-17 11:19:51CU大牛徽章
日期:2013-04-17 11:19:42CU大牛徽章
日期:2013-04-17 11:19:37CU大牛徽章
日期:2013-04-17 11:19:32CU大牛徽章
日期:2013-04-17 11:19:28
7 [报告]
发表于 2013-07-18 22:57 |只看该作者
gnah 发表于 2013-07-18 12:58
这不是线程安全的问题,而是线程执行顺序不确定


最后的数据结果不一致,应该是由数据丢失了,我做了一个没有磁盘IO的例子,不存在这个问题
我试一下用虚拟磁盘吧

论坛徽章:
19
CU大牛徽章
日期:2013-03-13 15:32:35CU大牛徽章
日期:2013-09-18 15:15:15CU大牛徽章
日期:2013-05-20 10:46:44CU大牛徽章
日期:2013-05-20 10:46:38CU大牛徽章
日期:2013-05-20 10:46:31CU大牛徽章
日期:2013-05-20 10:46:25CU大牛徽章
日期:2013-05-20 10:46:18CU大牛徽章
日期:2013-04-17 11:19:51CU大牛徽章
日期:2013-04-17 11:19:42CU大牛徽章
日期:2013-04-17 11:19:37CU大牛徽章
日期:2013-04-17 11:19:32CU大牛徽章
日期:2013-04-17 11:19:28
8 [报告]
发表于 2013-07-18 23:08 |只看该作者


这是用虚拟盘软件制作了一个虚拟磁盘,盘符是F:


将所有的文件都放在虚拟盘中,运算结果还是不一致


论坛徽章:
19
CU大牛徽章
日期:2013-03-13 15:32:35CU大牛徽章
日期:2013-09-18 15:15:15CU大牛徽章
日期:2013-05-20 10:46:44CU大牛徽章
日期:2013-05-20 10:46:38CU大牛徽章
日期:2013-05-20 10:46:31CU大牛徽章
日期:2013-05-20 10:46:25CU大牛徽章
日期:2013-05-20 10:46:18CU大牛徽章
日期:2013-04-17 11:19:51CU大牛徽章
日期:2013-04-17 11:19:42CU大牛徽章
日期:2013-04-17 11:19:37CU大牛徽章
日期:2013-04-17 11:19:32CU大牛徽章
日期:2013-04-17 11:19:28
9 [报告]
发表于 2013-07-18 23:09 |只看该作者
试一下楼上建议的CompletionService吧
感觉这个问题很怪异,我试了另外一个例子完全没有问题,是个算素数的,没有磁盘IO操作,是运算密集型的例子

论坛徽章:
19
CU大牛徽章
日期:2013-03-13 15:32:35CU大牛徽章
日期:2013-09-18 15:15:15CU大牛徽章
日期:2013-05-20 10:46:44CU大牛徽章
日期:2013-05-20 10:46:38CU大牛徽章
日期:2013-05-20 10:46:31CU大牛徽章
日期:2013-05-20 10:46:25CU大牛徽章
日期:2013-05-20 10:46:18CU大牛徽章
日期:2013-04-17 11:19:51CU大牛徽章
日期:2013-04-17 11:19:42CU大牛徽章
日期:2013-04-17 11:19:37CU大牛徽章
日期:2013-04-17 11:19:32CU大牛徽章
日期:2013-04-17 11:19:28
10 [报告]
发表于 2013-07-18 23:40 |只看该作者
回复 5# zavakid
  1. public double computeNAV() {
  2.                 double NAV = 0.0;
  3.                 int poolSize = getPoolSize();
  4.                 ExecutorService executorService = Executors
  5.                                 .newFixedThreadPool(poolSize);
  6.                 CompletionService<Double> completionService = new ExecutorCompletionService<Double>(
  7.                                 executorService);
  8.                 for (final String ticker : stocks.keySet()) {
  9.                         completionService.submit(new Callable<Double>() {
  10.                                 public Double call() throws Exception {
  11.                                         return stocks.get(ticker)
  12.                                                         * finance.getPrice(ticker, YahooFinance.ID);
  13.                                 }
  14.                         });
  15.                 }
  16.                 for (int i = 0; i < stocks.size(); i++) {
  17.                         try {
  18.                                 NAV += completionService.take().get();
  19.                         } catch (InterruptedException | ExecutionException e) {
  20.                                 e.printStackTrace();
  21.                         }
  22.                 }
  23.                 executorService.shutdown();
  24.                 finance.close();
  25.                 return NAV;
  26.         }
复制代码
我从网上看了一个例子,是这么个用法,可是运行起来还是老毛病,结果总是老变
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP