关于Executors创建和管理线程的那些事
2017-09-28 15:05
495 查看
线程是并发最基本的单元。Java线程本质上被映射到操作系统线程,并且每个线程对象对应着一个计算机底层线程。
来张图说明一下:
1.自己管理线程并不是很好的选择,最大劣势是,你很容易过分的关注线程的数量。线程是很昂贵的对象,创建它们需要耗费大量的内存和时间。线程太少,你不能获得良好的并发性;线程太多,将很可能导致内存问题,调度也变得更复杂。
2.幸运的是,JVM为我们提供了线程管理的功能,它就是Executor接口。JAVA1.5后引入的Executor框架的最大优点是把任务的提交和执行解耦。
Executor接口的定义非常简单:
public interface Executor {
void execute(Runnable command);
}
它隐藏了如何处理Runnable的细节。作为开发者,我们只需要给它任务,Executor自会处理它。
Executors类提供了一组方法,能够创建拥有完善配置的线程池和executor。我们将使用newFixedThreadPool()创建预定义数量的线程,并不允许线程数量超过这个预定义值。这意味着,如果所有的线程都被使用的话,提交的命令将会被放到一个队列中等待;当然这是由executor来管理的。
如果你需要精确的控制程序产生的线程数量,以及它们的精确行为,那么executor和executor服务将是正确的选择。对于大型系统,我个人认为使用executor最合适。
3.Executor框架主要包含以下3个部分:
(1)任务:包括Runnable和Callable,其中Runnable表示一个可以异步执行的任务,而Callable表示一个会产生结果的任务
(2)执行:包括Executor框架的核心接口Executor以及其子接口ExecutorService。
(3)计算的结果:包括接口Future和其实现类FutureTask。
ThreadPoolExecutor类(java.util.concurrent.ThreadPoolExecutor):
它是线程池的核心实现类,用来执行被提交的任务。
它通常由工厂类Executors来创建,Executors可以创建单线程执行SingleThreadExecutor,固定线程数量线程池FixedThreadPool以及无限制线程数量线程池CachedThreadPool等不同的ThreadPoolExecutor。
举例,创建实例:
ExecutorService executorService = Executors.newCachedThreadPool();
ExecutorService executorService = Executors.newFixedThreadPool(3);
ExecutorService executorService = Executors.newSingleThreadExecutor();
关闭线程: executorService.shutdown();
使用Callable,Future返回结果
Future<V>代表一个异步执行的操作,通过get()方法可以获得操作的结果,如果异步操作还没有完成,则,get()会使当前线程阻塞。FutureTask<V>实现了Future<V>和Runable<V>。Callable代表一个有返回值得操作。
ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。
举例:
1. public class MytestExectorService {
2.
3. private ExecutorService exec;
4. private int cpuCoreNumber;
5. private List<Future<Long>> tasks = new ArrayList<Future<Long>>();
6.
7.
8. class SumCalculator implements Callable<Long> {
9. private int[] numbers;
10. private int start;
11. private int end;
12.
13. public SumCalculator(final int[] numbers, int start, int end) {
14. this.numbers = numbers;
15. this.start = start;
16. this.end = end;
17. }
18.
19. public Long call() throws Exception {
20. Long sum = 0l;
21. for (int i = start; i < end; i++) {
22. sum += numbers[i];
23. }
24. return sum;
25. }
26. }
27.
28. public MytestExectorService() {
29. cpuCoreNumber = Runtime.getRuntime().availableProcessors();
30. exec = Executors.newFixedThreadPool(cpuCoreNumber);
31. }
32.
33. public Long sum(final int[] numbers) {
34.
35. for (int i = 0; i < cpuCoreNumber; i++) {
36. int increment = numbers.length / cpuCoreNumber + 1;
37. int start = increment * i;
38. int end = increment * i + increment;
39. if (end > numbers.length)
40. end = numbers.length;
41. SumCalculator subCalc = new SumCalculator(numbers, start, end);
42. FutureTask<Long> task = new FutureTask<Long>(subCalc);
43. tasks.add(task);
44. if (!exec.isShutdown()) {
45. exec.submit(task);
46. }
47. }
48. return getResult();
49. }
50.
51.
56. public Long getResult() {
57. Long result = 0l;
58.
4000
for (Future<Long> task : tasks) {
59. try {
60. // 如果计算未完成则阻塞
61. Long subSum = task.get();
62. result += subSum;
63. } catch (InterruptedException e) {
64. e.printStackTrace();
65. } catch (ExecutionException e) {
66. e.printStackTrace();
67. }
68. }
69. return result;
70. }
71.
72. public void close() {
73. exec.shutdown();
74. }
75. }
main方法:
public static void main(String[] args) throws ExecutionException {
int[] numbers = new int[] { 1, 2, 0,,22,3, 4, 5, 6, 8 };
MytestExectorService calc = new MytestExectorService();
Long sum = calc.sum(numbers);
System.out.println(sum);
calc.close();
}
来张图说明一下:
1.自己管理线程并不是很好的选择,最大劣势是,你很容易过分的关注线程的数量。线程是很昂贵的对象,创建它们需要耗费大量的内存和时间。线程太少,你不能获得良好的并发性;线程太多,将很可能导致内存问题,调度也变得更复杂。
2.幸运的是,JVM为我们提供了线程管理的功能,它就是Executor接口。JAVA1.5后引入的Executor框架的最大优点是把任务的提交和执行解耦。
Executor接口的定义非常简单:
public interface Executor {
void execute(Runnable command);
}
它隐藏了如何处理Runnable的细节。作为开发者,我们只需要给它任务,Executor自会处理它。
Executors类提供了一组方法,能够创建拥有完善配置的线程池和executor。我们将使用newFixedThreadPool()创建预定义数量的线程,并不允许线程数量超过这个预定义值。这意味着,如果所有的线程都被使用的话,提交的命令将会被放到一个队列中等待;当然这是由executor来管理的。
如果你需要精确的控制程序产生的线程数量,以及它们的精确行为,那么executor和executor服务将是正确的选择。对于大型系统,我个人认为使用executor最合适。
3.Executor框架主要包含以下3个部分:
(1)任务:包括Runnable和Callable,其中Runnable表示一个可以异步执行的任务,而Callable表示一个会产生结果的任务
(2)执行:包括Executor框架的核心接口Executor以及其子接口ExecutorService。
(3)计算的结果:包括接口Future和其实现类FutureTask。
ThreadPoolExecutor类(java.util.concurrent.ThreadPoolExecutor):
它是线程池的核心实现类,用来执行被提交的任务。
它通常由工厂类Executors来创建,Executors可以创建单线程执行SingleThreadExecutor,固定线程数量线程池FixedThreadPool以及无限制线程数量线程池CachedThreadPool等不同的ThreadPoolExecutor。
举例,创建实例:
ExecutorService executorService = Executors.newCachedThreadPool();
ExecutorService executorService = Executors.newFixedThreadPool(3);
ExecutorService executorService = Executors.newSingleThreadExecutor();
关闭线程: executorService.shutdown();
使用Callable,Future返回结果
Future<V>代表一个异步执行的操作,通过get()方法可以获得操作的结果,如果异步操作还没有完成,则,get()会使当前线程阻塞。FutureTask<V>实现了Future<V>和Runable<V>。Callable代表一个有返回值得操作。
ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。
举例:
1. public class MytestExectorService {
2.
3. private ExecutorService exec;
4. private int cpuCoreNumber;
5. private List<Future<Long>> tasks = new ArrayList<Future<Long>>();
6.
7.
8. class SumCalculator implements Callable<Long> {
9. private int[] numbers;
10. private int start;
11. private int end;
12.
13. public SumCalculator(final int[] numbers, int start, int end) {
14. this.numbers = numbers;
15. this.start = start;
16. this.end = end;
17. }
18.
19. public Long call() throws Exception {
20. Long sum = 0l;
21. for (int i = start; i < end; i++) {
22. sum += numbers[i];
23. }
24. return sum;
25. }
26. }
27.
28. public MytestExectorService() {
29. cpuCoreNumber = Runtime.getRuntime().availableProcessors();
30. exec = Executors.newFixedThreadPool(cpuCoreNumber);
31. }
32.
33. public Long sum(final int[] numbers) {
34.
35. for (int i = 0; i < cpuCoreNumber; i++) {
36. int increment = numbers.length / cpuCoreNumber + 1;
37. int start = increment * i;
38. int end = increment * i + increment;
39. if (end > numbers.length)
40. end = numbers.length;
41. SumCalculator subCalc = new SumCalculator(numbers, start, end);
42. FutureTask<Long> task = new FutureTask<Long>(subCalc);
43. tasks.add(task);
44. if (!exec.isShutdown()) {
45. exec.submit(task);
46. }
47. }
48. return getResult();
49. }
50.
51.
56. public Long getResult() {
57. Long result = 0l;
58.
4000
for (Future<Long> task : tasks) {
59. try {
60. // 如果计算未完成则阻塞
61. Long subSum = task.get();
62. result += subSum;
63. } catch (InterruptedException e) {
64. e.printStackTrace();
65. } catch (ExecutionException e) {
66. e.printStackTrace();
67. }
68. }
69. return result;
70. }
71.
72. public void close() {
73. exec.shutdown();
74. }
75. }
main方法:
public static void main(String[] args) throws ExecutionException {
int[] numbers = new int[] { 1, 2, 0,,22,3, 4, 5, 6, 8 };
MytestExectorService calc = new MytestExectorService();
Long sum = calc.sum(numbers);
System.out.println(sum);
calc.close();
}
相关文章推荐
- Java:使用Executors创建和管理线程
- Java:使用Executors创建和管理线程
- Java:使用Executors创建和管理线程 推荐
- Java:使用Executors创建和管理线程
- 关于Executors.newFixedThreadPool何时创建新线程
- Java:使用Executors创建和管理线程
- Java:使用Executors创建和管理线程
- 使用Executors创建和管理线程
- 使用Executors创建和管理线程
- Java:使用Executors创建和管理线程
- Java:使用Executors创建和管理线程
- Java:使用Executors创建和管理线程
- Java:使用Executors创建和管理线程
- 使用Executors创建和管理线程
- Java:使用Executors创建和管理线程
- Java:使用Executors创建和管理线程
- 多线程并发 ——使用Executors创建和管理线程
- Java:使用Executors创建和管理线程
- Java:使用Executors创建和管理线程
- 多线程Java:使用Executors创建和管理线程