【问题标题】:Java multithreading for the purpose of simulating data用于模拟数据的Java多线程
【发布时间】:2016-03-16 19:50:52
【问题描述】:

所以我目前正在创建一个数据分析和预测程序,出于测试目的,我正在模拟大量数据(在 10,000 - 1,000,000 范围内)“试验”。数据是理论游戏的模拟比赛。每场比赛都有回合。该程序的基本伪代码是这样的:

main(){
    data = create(100000);
    saveToFile(data);
}

Data create(){
    Data returnData = new Data(playTestMatch());
}

Match playTestMatch(){


    List<Round> rounds = new List<Round>();

    while(!GameFinished){
        rounds.add(playTestRound());
    }

    Match returnMatch = new Match(rounds);
}


Round playTestRound(){
    //Do round stuff
}

现在,我想知道是否可以在多个线程上处理这些回合的模拟以加快进程。我不熟悉多线程背后的理论,所以请有人帮我完成这个,或者向我解释为什么这不起作用(不会加快进程)。谢谢!

【问题讨论】:

  • 你的代码是thread-safe吗?
  • 参见 java.util.concurrent Executors 和 ExecutorService
  • 使用Amdahl's Law 确定您的代码并行化多少可以加快速度。

标签: java multithreading statistics analytics simulation


【解决方案1】:

如果您是 Java 多线程的新手,这个解释一开始可能有点难以理解,但我会尽量让它看起来简单。

基本上,我认为通常每当您拥有大型数据集时,与使用单线程方法相反,使用多个线程同时运行操作确实会显着加快处理速度,但当然也有例外。

你需要考虑三件事:

  1. 创建线程

  2. 管理线程

  3. 每个线程与主线程计算的通信/共享结果

创建线程: 可以手动创建线程,扩展 Thread 类,也可以使用 Executors 类。 我更喜欢 Executors 类来创建线程,因为它允许您创建线程池并为您进行线程管理。也就是说,它将允许您重用线程池中空闲的现有线程,从而减少应用程序的内存占用。 您还必须查看 ExecutorService 接口,因为您将使用它来激发您的任务。

管理线程: Executors/Executors 服务在自动管理线程方面做得很好,所以如果你使用它,你不必太担心线程管理。

沟通:这是整个过程的关键部分。在这里,您必须非常详细地考虑应用程序的线程安全性。

我建议使用两个队列来完成这项工作,一个用于读取数据的读取队列和一个用于写入数据的写入队列。

但是,如果您使用的是简单的数组列表,请确保通过将数组列表包含在同步块中来同步您的代码以确保线程安全

synchronized(arrayList){
 // do stuff

}

【讨论】:

  • 所以基本上我继续尝试靠耳朵去做。据我所见,它不会崩溃和燃烧,并且显着提高了速度。如果您有兴趣,这是我的 GitHub Repo 链接。 github.com/Schwaitz/CSGORoundAnalysis
  • 介意告诉我为什么尽管没有使用 synchronized() 块,但它没有崩溃和烧毁吗?
  • @kuuy 您访问“匹配”数组列表的方式并不理想,在中等负载的实时系统中会导致崩溃。
  • 我将简要解释一下为什么以及为什么它在您的情况下没有崩溃。静态变量存储在 Java 堆的 PermGen 部分中。 PermGen 默认情况下非常小,客户端模式为 32MB,服务器模式为 64MB。当您运行一个在内存中存储大量数据的大规模并行应用程序时,您可以看到在此部分中您将很快耗尽空间,并且应用程序会崩溃并显示 OutOfMemoryError。
  • 在您的情况下,它没有崩溃可能是因为您的数据集太小,所以没有真正填满 PermGen 空间,或者可能是您增加了 PermGen 大小:-XX:MaxPermSize=XXXM。无论哪种情况,您都不会注意到崩溃。
【解决方案2】:

如果您的代码是线程安全的,并且您可以将任务拆分为彼此不依赖的离散块,那么这相对容易。将执行工作的类设为 Callable 并将工作块添加到 List,然后使用 ExecutorService,如下所示:

ArrayList<Simulation> SL=new ArrayList<Simulation>();
for(int i=0; i<chunks; i++)
    SL.add(new Simulation(i));
ExecutorService executor=Executors.newFixedThreadPool(nthreads);//how many threads
List<Future<Result>> results=null;
try {
     results = executor.invokeAll(SL);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
executor.shutdown();
for(Future<Result> result:results)
  result.print();
    

因此,模拟是可调用的并返回一个结果,结果是一个列表,当使用模拟的 ArrayList 调用 executor.invokeAll 时会填充该列表。获得结果后,您可以打印它们或其他任何东西。可能最好将 nthreads 设置为等于您可用的内核数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-06-16
    • 1970-01-01
    • 2017-11-20
    • 1970-01-01
    • 1970-01-01
    • 2011-09-14
    • 1970-01-01
    • 2010-10-30
    相关资源
    最近更新 更多