困惑于Java在多处理过程中如何共享变量

时间:2012-02-24 00:51:00

标签: java multiprocessing

我刚刚开始使用java,很抱歉,如果这个问题的答案很明显。我无法弄清楚如何在java中共享变量。我一直在使用python,并希望尝试将一些代码移植到Java以更好地学习语言。很多我的代码被移植但我不确定多少处理和变量共享在Java中是如何工作的(我的进程不受磁盘限制,并且使用了大量的cpu和搜索列表)。

在Python中,我可以这样做:

from multiprocessing import Pool, Manager
manager = Manager()
shared_list = manager.list()
pool = Pool(process=4) 
for variables_to_send in list_of_data_to_process:
       pool.apply_async(function_or_class, (variables_to_send, shared_list))
pool.close()
pool.join()

我在查找如何在Java中进行多处理和共享时遇到了一些麻烦。这个question帮助我理解了一下(通过代码)实现runnable如何帮助我开始认为java可能会自动多处理线程(如果我错了就纠正我,我读到一旦线程超过容量一个cpu他们被转移到另一个CPU?oracle文档似乎更专注于线程而不是多处理)。但它没有解释如何在进程之间共享列表或其他变量(并使它们保持足够接近的同步)。

有任何建议或资源吗?我希望我正在寻找错误的东西(多处理java),并希望这与我上面的代码一样容易(或类似简单)。

谢谢!

3 个答案:

答案 0 :(得分:3)

线程和进程之间存在重要差异,您现在正在遇到它:除了一些例外情况,线程共享内存,但进程不会

请注意,真正的操作系统可以解决我即将要说的所有内容,但在典型情况下不会使用这些功能。因此,要启动一个新进程,必须使用系统调用以某种方式克隆当前进程(在* nix上,这是fork()),然后替换代码,堆栈,命令行参数等带有另一个系统调用的子进程的调用(在* nix上,这是exec()系列调用)。 Windows与这两个系统调用大致相同,所以我所说的一切都是跨平台的。此外,Java Runtime Environment负责所有这些系统调用,如果没有JNI或其他一些互操作技术,您无法自己真正执行它们。

有关此模型的两个重要注意事项:子进程不共享父进程的地址空间,并且exec()调用将替换子进程的整个地址空间。因此,父进程中的变量对子进程不可用,反之亦然。

线程模型完全不同。线程有点像精简进程,因为每个线程都有自己的指令指针,并且(在大多数系统上)线程由操作系统调度程序调度。但是,线程是进程的一部分。每个进程至少有一个线程,进程中的所有线程共享内存。

现在问题:

Python代理示例显示,Python多处理模块只需很少的工作即可生成进程。在Java中,产生一个新进程需要更多的工作。它涉及使用ProcessProcessBuilder.start()创建新的Runtime.exec()对象。然后,您可以将字符串传递给子进程,获取其输出,等待它退出,以及一些其他通信原语。我建议编写一个程序作为协调员并启动每个子进程,并编写一个大致对应于示例中function_or_class的工作程序。协调员可以打开工作程序的多个副本,为每个工作程序分配任务,并等待所有工作人员完成。

答案 1 :(得分:1)

您可以将Java Thread用于此目的。您需要创建一个用户定义的类。该类应该有setter方法,您可以通过它设置shared_list对象。实现Runnable接口并在run()方法中执行处理任务。你可以在互联网上找到很好的例子。如果要共享同一个shared_list实例,则需要确保同步对此变量的访问。

答案 2 :(得分:1)

这不是在java中使用线程的最简单方法,但它对你发布的python代码是封闭的。任务类是可调用接口的实例,它有一个调用方法。当我们创建10000个Task实例中的每一个时,我们将它们传递给同一个列表。因此,当调用所有这些对象的调用方法时,它们将使用相同的列表。

我们在这里使用固定大小的4个线程的线程池,因此我们提交的所有任务都会排队等待线程可用。

public class SharedListRunner {
    public void RunList() {
        ExecutorService executerService = Executors.newFixedThreadPool(4);
        List<String> sharedList = new List<String>();
        sharedList.add("Hello");
        for(int i=0; i < 10000; i++)
            executerService.submit(new Task(list));
    }
}

public class Task implements Callable<String> {

    List<String> sharedList;    

    public Task(List<String> sharedList) {
            this.sharedList = sharedList;
    }

    @Override
    public String call() throws Exception {
            //Do something to shared list
            sharedList.size();  
            return "World";
    }
}

在任何时候,4个线程都在访问列表。如果你想进一步挖掘4个Java线程正在访问列表,那么为这4个java线程提供服务的操作系统线程可能更少,而且每个CPU的核心通常有2个或4个更少的处理器线程。