Java Executor 框架最佳实践 开发者秘籍_开

在 JDK 5 中发布了 Executors framework(java.util.concurrent.Executor)来运行 Runnable 对象,不用像之前那样每次都创建新的线程,大多数情况下会复用已经创建的线程。

我们都知道在java中创建线程有两种方法。如果你想更多地阅读他们的比较,阅读这篇文章。在java中创建线程是一个非常昂贵的过程,它还包括内存开销。因此,如果我们可以重复使用这些线程,来运行 Runnable 可以更节省资源。

在本文中,我将编写一些示例程序,演示如何使用 Executor 框架,然后我们将讨论在设计多线程程序时需要记住的一些最佳实践。

如果你想了解更多关于其他多线程方面的信息,请点击此链接

基本使用示例

在我们的示例程序中,有两个任务运行,预计都不会停止运行,并且两个任务都在固定的环境下运行。 我将尝试编写一个包装类,以便于:

  1. 如果任何任务引发异常,程序将捕获它并重新启动任务。
  2. 如果任何任务运行完成,程序将通知并重新启动任务。

以下是程序的代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
复制代码package com.devcheats.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();
}
}
}
}

请不要忘记在文章末尾阅读最佳实践。

在单个线程中执行多个任务

每个Runnable都不必在单独的线程中执行。有时,我们需要在单个线程中执行多个任务,每个任务都是Runnable的实例。要设计这种类型的解决方案,应该使用多运行。这个多可运行只是一个需要执行的可运行集合。除此之外,这个多runnable也是一个Runnable本身。

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

不需要在单独的线程中执行每一个 Runnable。有时候我们需要在一个线程中执行多个任务,每个任务都是一个 Runnable 实例。 要设计这种类型的程序,应该创建多个 Runnable,这些 Runnable 是需要被执行的任务集合。 另外,这个 Runnable 集合本身也是 Runnable 实例。

下面是需要在单个线程中执行多个任务的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
复制代码package com.devcheats.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();
}
}
}

我们创建一个多任务运行的包装器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
复制代码package com.devcheats.demo.multithreading;

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();
}
}
}

现在上面的多线程可以像下面的程序这样执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
复制代码package com.devcheats.demo.multithreading;

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 ! ");
}
}

必须遵守的最佳实践

  1. 总是使用一些静态分析工具来运行你的Java代码,比如 PMDFindBugs 来发现深层的问题,它们对决定未来可能出现的复杂场景更有帮助。
  2. 总是和高级开发人员相互进行代码审查,以便于在代码中发现死锁或活锁,在程序中添加一个健康监视器来检查运行任务的状态,大多数场景中这样做是好的方式。
  3. 编写多线程应用时要养成捕获错误的习惯,不仅仅是异常。有时会发生意想不到的事情,可能Java会向你抛出一个Error,它不是一个异常。
  4. 使用回滚开关,如果程序出错的时候可能是无法恢复的,你不必急于启动另一个循环来升级这种情况,相反应该等待程序恢复后再重新开始。
  5. 请注意,Executors 的核心是抽象出执行的细节,因此除非明确说明,否则不保证排序。

祝你学习愉快!


原文出处:https://howtodoinjava.com/core-java/multi-threading/java-executor-framework-tutorial-and-best-practices

王爵nice 翻译

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%