# Java 执行器框架教程和最佳实践

> 原文： [https://howtodoinjava.com/java/multi-threading/executor-framework-tutorial/](https://howtodoinjava.com/java/multi-threading/executor-framework-tutorial/)

与 JDK 5 一起发布的 [Java 执行器框架](https://docs.oracle.com/javase/tutorial/essential/concurrency/executors.html "Executor framework")（`java.util.concurrent.Executor`）用于运行`Runnable`对象，而无需每次都创建新线程，并且主要是重新使用已经创建的线程。

我们都知道有两种方法可以在 Java 中创建线程。 如果您想了解有关它们比较的更多信息，请阅读[**如何在 Java**](//howtodoinjava.com/java/multi-threading/difference-between-implements-runnable-and-extends-thread-in-java/ "Difference between “implements Runnable” and “extends Thread” in java")中创建线程。

在 Java 中创建线程是一个非常昂贵的过程，其中还包括内存开销。 因此，如果我们可以在创建后重新使用这些线程来运行将来的可运行对象，则是一个好主意。 在此**执行器框架教程**中，我将编写一些演示程序来演示`Executor`的用法，然后我们将讨论在设计下一个多线程应用程序时需要牢记的一些最佳实践。

## 1\. Java 执行器框架示例

在演示应用程序中，我们有两个任务正在运行。 两者都不会终止，并且都应在应用程序的生命周期内运行。 我将尝试编写一个主包装器类，例如：

*   如果有任何任务引发异常，则应用程序将捕获该异常并重新启动该任务。
*   如果有任何任务运行完毕，应用程序将注意到并重新启动任务。

下面是上述所需应用程序的代码示例。

```java
package com.howtodoinjava.multiThreading.executors;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class DemoExecutorUsage {

	private static ExecutorService executor = null;
	private static volatile Future taskOneResults = null;
	private static volatile Future taskTwoResults = null;

	public static void main(String[] args) {
		executor = Executors.newFixedThreadPool(2);
		while (true)
		{
			try
			{
				checkTasks();
				Thread.sleep(1000);
			} catch (Exception e) {
				System.err.println("Caught exception: " + e.getMessage());
			}
		}
	}

	private static void checkTasks() throws Exception {
		if (taskOneResults == null
				|| taskOneResults.isDone()
				|| taskOneResults.isCancelled())
		{
			taskOneResults = executor.submit(new TestOne());
		}

		if (taskTwoResults == null
				|| taskTwoResults.isDone()
				|| taskTwoResults.isCancelled())
		{
			taskTwoResults = executor.submit(new TestTwo());
		}
	}
}

class TestOne implements Runnable {
	public void run() {
		while (true)
		{
			System.out.println("Executing task one");
			try
			{
				Thread.sleep(1000);
			} catch (Throwable e) {
				e.printStackTrace();
			}
		}

	}
}

class TestTwo implements Runnable {
	public void run() {
		while (true)
		{
			System.out.println("Executing task two");
			try
			{
				Thread.sleep(1000);
			} catch (Throwable e) {
				e.printStackTrace();
			}
		}
	}
}

```

请不要忘记在帖子结尾阅读最佳实践。

## 2\. Java 执行器框架 – `MultiRunnable`

不必每个`Runnable`都在单独的线程中执行。 有时，我们需要在单个线程中执行多个作业，并且每个作业都是`Runnable`的实例。 要设计此类解决方案，应使用`MultiRunnable`。 这个`MultiRunnable`对象不过是需要执行的可运行对象的集合。 唯一的补充是，该`MultiRunnable`库本身也是`Runnable`。

以下是需要在单个线程中执行的任务列表。

```java
package com.howtodoinjava.multiThreading.executors;

public class TaskOne implements Runnable {
	@Override
	public void run() {
		System.out.println("Executing Task One");
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

public class TaskTwo implements Runnable {
	@Override
	public void run() {
		System.out.println("Executing Task Two");
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

public class TaskThree implements Runnable {
	@Override
	public void run() {
		System.out.println("Executing Task Three");
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

```

让我们创建上述任务的`MultiRunnable`包装器。

```java
package com.howtodoinjava.demo.multiThread;

import java.util.List;

public class MultiRunnable implements Runnable {

    private final List<Runnable> runnables;

    public MultiRunnable(List<Runnable> runnables) {
        this.runnables = runnables;
    }

    @Override
    public void run() {
        for (Runnable runnable : runnables) {
        	 new Thread(runnable).start();
        }
    }
}

```

现在可以在下面的程序中以这种方式执行上面的`multi runnable`：

```java
package com.howtodoinjava.demo.multiThread;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MultiTaskExecutor {

    public static void main(String[] args) {

        BlockingQueue<Runnable> worksQueue = new ArrayBlockingQueue<Runnable>(10);
        RejectedExecutionHandler rejectionHandler = new RejectedExecutionHandelerImpl();
        ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 3, 10, TimeUnit.SECONDS, worksQueue, rejectionHandler);

        executor.prestartAllCoreThreads();

        List<Runnable> taskGroup = new ArrayList<Runnable>();
        taskGroup.add(new TestOne());
        taskGroup.add(new TestTwo());
        taskGroup.add(new TestThree());

        worksQueue.add(new MultiRunnable(taskGroup));
    }
}

class RejectedExecutionHandelerImpl implements RejectedExecutionHandler
{
    @Override
    public void rejectedExecution(Runnable runnable,
            ThreadPoolExecutor executor)
    {
        System.out.println(runnable.toString() + " : I've been rejected ! ");
    }
}

```

## 3\. Java 执行器框架最佳实践

1.  始终针对静态分析工具（例如 [PMD](http://pmd.sourceforge.net/ "pmd home page") 和 [FindBugs](http://findbugs.sourceforge.net/ "findbugs home page")）运行 Java 代码，以查找更深层次的问题。 它们对于确定将来可能出现的丑陋情况非常有帮助。
2.  始终与高级人员进行交叉检查并更好地计划代码审查，以在执行过程中检测并可能在代码中出现[死锁或活锁](https://en.wikipedia.org/wiki/Deadlock "deadlock and livelock")。 在大多数情况下，在应用程序中添加运行状况监视器以检查正在运行的任务的状态是一个很好的选择。
3.  在多线程程序中，也要养成捕获错误的习惯，而不仅仅是异常。 有时会发生意想不到的事情，除了异常之外，Java 还会向您抛出错误。
4.  使用退避开关，因此，如果出现问题并且无法恢复，您就不会急于启动另一个循环来升级情况。 相反，您需要等到情况恢复正常后再重新开始。
5.  请注意，执行器的全部目的是抽象出执行的细节，因此除非明确说明，否则不能保证顺序。

学习愉快！