前言
阅读本篇文章,你需要先理解以下知识:
- 第一章:Callable的使用(点我跳转)
- 多线程Thread的基本使用 (点我跳转)
- 线程池基本知识 (点我跳转)
- extends和implements
- 重写Override
- try catch错误处理
- Java基础知识
回顾
在上一章(点我跳转)我们了解了Callable
的基本使用,本次我们将把Callable
运用到线程池(点我跳转)中。
拷贝
用你的IDE新建一个项目或类,并将类命名为TestThreadPool
,然后将下面的代码替换到类中:
1import java.util.ArrayList;
2import java.util.List;
3import java.util.concurrent.*;
4
5public class TestThreadPool {
6 ExecutorService executorService = Executors.newFixedThreadPool(2);
7 public static void main(String[] args) {
8 //实例化类
9 TestThreadPool testThreadPool = new TestThreadPool();
10 //调用动态方法
11 testThreadPool.threadPool();
12 }
13
14 public void threadPool() {
15 Thread1 thread1 = new Thread1();
16 Thread2 thread2 = new Thread2();
17 //将Future包装进List,实现添加结果
18 List<Future> resultList = new ArrayList<Future>();
19 for (int i = 0; i < 3; i++) {
20 System.out.println("线程池已提交:" + i);
21 Future res1 = executorService.submit(thread1);
22 Future res2 = executorService.submit(thread2);
23 //将获取的结果添加进List
24 resultList.add(res1);
25 resultList.add(res2);
26 }
27 System.out.println("正在关闭线程池...");
28 executorService.shutdown();
29 System.out.println("线程池已关闭.");
30 //executorService.shutdownNow();
31 //线程池运行结束,打印结果
32 for (int i = 0; i < resultList.size(); i++) {
33 Future future = resultList.get(i);
34 try {
35 System.out.println(future.get());
36 } catch (InterruptedException | ExecutionException e) {}
37 }
38 }
39}
40
41/**
42 * 线程1
43 */
44class Thread1 implements Callable {
45 @Override
46 public Object call() throws Exception {
47 try {
48 Thread.sleep(500);
49 } catch (Exception e) {}
50 return "本条数据来自线程1";
51 }
52}
53
54/**
55 * 线程2
56 */
57class Thread2 implements Callable {
58 @Override
59 public Object call() throws Exception {
60 try {
61 Thread.sleep(500);
62 } catch (Exception e) {}
63 return "本条数据来自线程2";
64 }
65}
对比
和线程池第二章的文章(点我跳转)中的实例代码对比一下,你会发现它们大概是相同的,此时再回顾一下Callable中的实例代码(点我跳转),你会发现这篇是这两篇的结合。
区别
定义的两个线程类:
1/**
2 * 线程1
3 */
4class Thread1 implements Callable {
5 @Override
6 public Object call() throws Exception {
7 try {
8 Thread.sleep(500);
9 } catch (Exception e) {}
10 return "本条数据来自线程1";
11 }
12}
13
14/**
15 * 线程2
16 */
17class Thread2 implements Callable {
18 @Override
19 public Object call() throws Exception {
20 try {
21 Thread.sleep(500);
22 } catch (Exception e) {}
23 return "本条数据来自线程2";
24 }
25}
你会发现它不再使用Runnable
了,而是使用了Callable
以支持返回数据。并且重写的方法不再是run()
,而是call()
。我们使用return
返回了String
类型的字符串。
调用方法
1public void threadPool() {
2 Thread1 thread1 = new Thread1();
3 Thread2 thread2 = new Thread2();
4 //将Future包装进List,实现添加结果
5 List<Future> resultList = new ArrayList<Future>();
6 for (int i = 0; i < 3; i++) {
7 System.out.println("线程池已提交:" + i);
8 Future res1 = executorService.submit(thread1);
9 Future res2 = executorService.submit(thread2);
10 //将获取的结果添加进List
11 resultList.add(res1);
12 resultList.add(res2);
13 }
14 System.out.println("正在关闭线程池...");
15 executorService.shutdown();
16 System.out.println("线程池已关闭.");
17 //executorService.shutdownNow();
18 //线程池运行结束,打印结果
19 for (int i = 0; i < resultList.size(); i++) {
20 Future future = resultList.get(i);
21 try {
22 System.out.println(future.get());
23 } catch (InterruptedException | ExecutionException e) {}
24 }
25 }
该方法仍是使用了同样的线程池,但执行方法使用了submit()
而不是execute()
。因为execute()
方法只支持Runnable
,请注意。
我们将Future
套入了一个List
中,以便异步循环写入
每个线程执行后返回的结果。
请仔细阅读调试,这并不难理解。
运行!
现在,运行你的代码,你会看到以下结果:
1线程池已提交:0
2线程池已提交:1
3线程池已提交:2
4正在关闭线程池...
5线程池已关闭.
6本条数据来自线程1
7本条数据来自线程2
8本条数据来自线程1
9本条数据来自线程2
10本条数据来自线程1
11本条数据来自线程2
后语
Java线程与线程池的知识点实际上是很多的。使用多线程是为了拥有更强的性能和更灵活的调用能力,同时也是每个合格的程序员必会的知识点。