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

Java 强制多个线程在可用时使用多个CPU,java,concurrency,multithreading,multicore,Java,Concurrency,Multithreading,Multicore,我正在编写一个Java程序,由于它的性质,它使用了大量的CPU。然而,很多程序可以并行运行,我已经使我的程序成为多线程的。当我运行它时,它似乎只使用一个CPU,直到它需要更多的CPU,然后再使用另一个CPU。在Java中,我可以做些什么来强制不同的线程在不同的内核/CPU上运行?最简单的事情就是将程序分解为多个进程。操作系统将在内核之间分配它们 更难的是将程序分成多个线程,并信任JVM正确分配它们。这通常是人们利用可用硬件所做的事情 编辑 多处理程序如何“更容易”?这是管道中的一个步骤 pub

我正在编写一个Java程序,由于它的性质,它使用了大量的CPU。然而,很多程序可以并行运行,我已经使我的程序成为多线程的。当我运行它时,它似乎只使用一个CPU,直到它需要更多的CPU,然后再使用另一个CPU。在Java中,我可以做些什么来强制不同的线程在不同的内核/CPU上运行?

最简单的事情就是将程序分解为多个进程。操作系统将在内核之间分配它们

更难的是将程序分成多个线程,并信任JVM正确分配它们。这通常是人们利用可用硬件所做的事情


编辑

多处理程序如何“更容易”?这是管道中的一个步骤

public class SomeStep {
    public static void main( String args[] ) {
        BufferedReader stdin= new BufferedReader( System.in );
        BufferedWriter stdout= new BufferedWriter( System.out );
        String line= stdin.readLine();
        while( line != null ) {
             // process line, writing to stdout
             line = stdin.readLine();
        }
    }
}
管道中的每个步骤都具有类似的结构。包括任何处理的9行开销

这可能不是绝对最有效的。但这很容易


并发进程的总体结构不是JVM问题。这是操作系统的问题,所以使用shell

java -cp pipline.jar FirstStep | java -cp pipline.jar SomeStep | java -cp pipline.jar LastStep
剩下的唯一一件事就是为管道中的数据对象进行一些序列化。
标准序列化工作得很好。有关如何序列化的提示,请阅读。您可以将
BufferedReader
BufferedWriter
替换为
ObjectInputStream
ObjectOutputStream
来实现这一点。

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

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

教程:

方法二:定义实现可运行(如果不返回值)或可调用(如果返回)接口的对象,这些接口包含处理代码。将这些作为任务从java.util.concurrent包传递给ExecutorService。java.util.concurrent.Executors类有一系列方法来创建标准的、有用的Executor服务。执行者教程

根据个人经验,Executors fixed&cached线程池非常好,不过您需要调整线程数。Runtime.getRuntime().availableProcessors()可在运行时用于计算可用内核数。应用程序完成后,您需要关闭线程池,否则应用程序将不会退出,因为线程池线程将保持运行

获得好的多核性能有时很棘手,而且充满了陷阱:

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());
        }
    }
}
  • 磁盘I/O在运行时会大大降低速度 平行的一次只能有一个线程执行磁盘读/写操作
  • 对象的同步为多线程操作提供了安全性,但会降低工作速度
  • 如果任务太多 琐碎的(小的工作位,执行 快速)管理它们的开销 在一个项目中,服务成本超过 您可以从多个核心中获益
  • 创建新线程对象很慢。如果可能,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());
        }
    }
}
公共类TaskThreader{
类DoStuff实现可调用{
反对;
公共对象调用(){
in=剂量步骤1(in);
in=剂量步骤2(in);
in=剂量步骤3(in);
返回;
}
公共DoStuff(对象输入){
in=输入;
}
}
公共抽象对象doStep1(对象输入);
公共抽象对象doStep2(对象输入);
公共抽象对象doStep3(对象输入);
公共静态void main(字符串[]args)引发异常{
ExecutorService exec=Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
ArrayList tasks=新建ArrayList();
用于(对象输入:输入){
添加(新DoStuff(输入));
}
列表结果=exec.invokeAll(任务);
exec.shutdown();
对于(未来f:结果){
写入(f.get());
}
}
}

您应该以大量可调用的形式编写程序,将其交给ExecutorService,并使用invokeAll(…)执行

然后,您可以在运行时从Executors类中选择合适的实现。建议调用Executors.newFixedThreadPool(),其数字大致对应于要保持忙碌的cpu核心数

当我运行它时,它似乎只使用 一个CPU,直到它需要更多的CPU 使用另一个CPU-是否有我需要的任何东西 可以在Java中强制执行不同的操作 要在不同服务器上运行的线程 核心/CPU

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

“有没有办法强迫……”的答案不是直接的。JVM和/或主机操作系统决定使用多少“本机”线程,以及如何将这些线程映射到物理处理器。您确实有一些调整选项。例如,我
/**
     * Creates a work-stealing thread pool using all
     * {@link Runtime#availableProcessors available processors}
     * as its target parallelism level.
     * @return the newly created thread pool
     * @see #newWorkStealingPool(int)
     * @since 1.8
     */
    public static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }