强制多个线程在可用时使用多个CPU

Nos*_*ama 66 java concurrency multithreading multicore

我正在编写一个使用大量CPU的Java程序,因为它的功能本质.但是,很多都可以并行运行,而且我的程序是多线程的.当我运行它时,它似乎只使用一个CPU,直到它需要更多然后它使用另一个CPU - 我有什么可以用Java来强制不同的线程在不同的核心/ CPU上运行吗?

Bob*_*Gee 56

Java中有多种基本的多线程方法.使用这些方法创建的每个逻辑任务都应在需要和可用时在新核心上运行.

方法一:定义一个Runnable或Thread对象(可以在构造函数中使用Runnable)并使用Thread.start()方法启动它.它将在操作系统提供的任何核心上执行 - 通常是负载较少的核心.

教程:定义和启动线程

方法二:定义实现Runnable的对象(如果它们不返回值)或Callable(如果它们)接口,它们包含您的处理代码.将这些作为任务从java.util.concurrent包传递给ExecutorService.java.util.concurrent.Executors类有许多方法来创建标准的,有用的ExecutorServices类.链接到执行者教程.

根据个人经验,Executors修复和缓存的线程池非常好,但您需要调整线程数.可以在运行时使用Runtime.getRuntime().availableProcessors()来计算可用内核.应用程序完成后,您需要关闭线程池,否则应用程序将不会退出,因为ThreadPool线程保持运行.

获得良好的多核性能有时很棘手,并且充满了问题:

  • 并行运行时,磁盘I/O会减慢LOT.一次只能有一个线程执行磁盘读/写操作.
  • 对象的同步为多线程操作提供了安全性,但减慢了工作速度.
  • 如果任务太简单(小工作位,快速执行),那么在ExecutorService中管理它们的开销比从多个核心获得的开销要高得多.
  • 创建新的Thread对象很慢.如果可能,ExecutorServices将尝试重用现有线程.
  • 当多个线程处理某些事情时,会发生各种各样的疯狂事情.保持系统简单,并尝试使任务在逻辑上不同且不相互作用.

另一个问题是:控制工作很难!一个好的做法是让一个管理器线程创建并提交任务,然后是一些工作线程与工作队列(使用ExecutorService).

我只是在谈论关键点 - 多线程编程被许多专家认为是最难编程的主题之一.它不直观,复杂,抽象通常很弱.


编辑 - 使用ExecutorService的示例:

public class TaskThreader {
    class DoStuff implements Callable {
       Object in;
       public Object call(){
         in = doStep1(in);
         in = doStep2(in);
         in = doStep3(in); 
         return in;
       }
       public DoStuff(Object input){
          in = input;
       }
    }

    public abstract Object doStep1(Object input);    
    public abstract Object doStep2(Object input);    
    public abstract Object doStep3(Object input);    

    public static void main(String[] args) throws Exception {
        ExecutorService exec = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
        ArrayList<Callable> tasks = new ArrayList<Callable>();
        for(Object input : inputs){
           tasks.add(new DoStuff(input));
        }
        List<Future> results = exec.invokeAll(tasks);
        exec.shutdown();
        for(Future f : results) {
           write(f.get());
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Ste*_*n C 29

当我运行它时,它似乎只使用一个CPU,直到它需要更多然后它使用另一个CPU - 我有什么可以用Java来强制不同的线程在不同的核心/ CPU上运行吗?

我将你问题的这一部分解释为你已经解决了使你的应用程序具有多线程能力的问题.尽管如此,它并没有立即开始使用多个核心.

"有没有办法强迫......"的答案是(AFAIK)不直接.您的JVM和/或主机操作系统决定使用多少"本机"线程,以及这些线程如何映射到物理处理器.你有一些调整选项.例如,我找到了这个页面,讨论如何调整Solaris上的Java线程.与此页关于可减缓了多线程应用程序的其他事情了会谈.


Zan*_*ynx 18

首先,您应该向自己证明您的程序在多个内核上运行得更快.许多操作系统尽可能地在同一个核心上运行程序线程.

在同一核心上运行有许多优点.CPU缓存很热,这意味着该程序的数据被加载到CPU中.锁定/监视/同步对象位于CPU缓存中,这意味着其他CPU不需要在总线上执行缓存同步操作(非常昂贵!).

有一件事可以很容易地让你的程序一直在同一个CPU上运行,就是过度使用锁和共享内存.你的线程不应该互相交谈.线程在同一内存中使用相同对象的频率越低,它们在不同CPU上运行的频率就越高.他们使用相同内存的次数越多,他们就越需要阻止等待其他线程.

每当操作系统看到另一个线程的一个线程块时,它就会在同一个CPU上运行该线程.它减少了在CPU间总线上移动的内存量.这就是我猜你在程序中看到的结果.


bri*_*gge 8

首先,我建议阅读Brian Goetz的"实践中的并发".

替代文字

这是迄今为止描述并发java编程的最佳书籍.

并发性"易于学习,难以掌握".在尝试之前,我建议阅读有关该主题的大量内容.很容易让多线程程序在99.9%的时间内正常工作,并且失败率为0.1%.但是,这里有一些提示可以帮助您入门:

有两种常用方法可以使程序使用多个核心:

  1. 使用多个进程运行程序.一个例子是使用Pre-Fork MPM编译的Apache,它将请求分配给子进程.在多进程程序中,默认情况下不共享内存.但是,您可以跨进程映射共享内存的各个部分.Apache用它的"记分牌"做到这一点.
  2. 使程序多线程.在多线程程序中,默认情况下共享所有堆内存.每个线程仍然拥有自己的堆栈,但可以访问堆的任何部分.通常,大多数Java程序都是多线程的,而不是多进程的.

在最低级别,可以创建和销毁线程.Java使得以便携式跨平台方式创建线程变得容易.

由于创建和销毁线程的成本往往很高,Java现在包括Executors来创建可重用的线程池.可以将任务分配给执行程序,并且可以通过Future对象检索结果.

通常,一个人可以将任务划分为较小的任务,但最终结果需要重新组合在一起.例如,使用合并排序,可以将列表划分为越来越小的部分,直到每个核心都进行排序.但是,由于每个子列表都已排序,因此需要将其合并以获取最终的排序列表.由于这是"分而治之"的问题相当普遍,因此有一个JSR框架可以处理底层分发和加入.该框架可能包含在Java 7中.