Java 中的多线程有两种基本方法。您使用这些方法创建的每个逻辑任务都应该在需要且可用时在新的核心上运行。
方法一:定义一个 Runnable 或 Thread 对象(可以在构造函数中接受 Runnable)并使用 Thread.start() 方法启动它运行。它将在操作系统提供的任何核心上执行——通常是负载较少的核心。
教程:定义和启动线程 http://java.sun.com/docs/books/tutorial/essential/concurrency/runthread.html
方法二:定义实现 Runnable(如果它们不返回值)或 Callable(如果它们返回值)接口的对象,其中包含您的处理代码。将这些作为任务从 java.util.concurrent 包传递给 ExecutorService。 java.util.concurrent.Executors 类有很多方法来创建标准的、有用的 ExecutorServices。Link http://java.sun.com/docs/books/tutorial/essential/concurrency/exinter.html执行者教程。
从个人经验来看,固定和缓存线程池的执行器非常好,尽管您需要调整线程计数。 Runtime.getRuntime().availableProcessors() 可在运行时用于计算可用内核数。当应用程序完成时,您需要关闭线程池,否则应用程序将不会退出,因为 ThreadPool 线程保持运行。
获得良好的多核性能有时很棘手,并且充满陷阱:
- 运行时磁盘 I/O 会减慢很多
平行线。一次只能有一个线程进行磁盘读/写。
- 对象同步为多线程操作提供了安全性,但会减慢工作速度。
- 如果任务太多
琐碎(小工作位,执行
快)管理它们的开销
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());
}
}
}