Java 对多线程编程提供了内置的支持并提供了良好的 API,经过使用 Thread
和 Runnable
两个基础类,咱们能够很方便的建立一个线程:java
Runnable runnable = new Runnable() { @Override public void run() { System.out.println("线程启动"); // 耗时操做 System.out.println("线程结束"); } }; Thread thread = new Thread(runnable); // 建立线程,runnable 做为线程要执行的任务(载体) thread.start(); // 启动线程 thread.join(); // 等待线程执行完毕
{ 题外话开始:编程
经过 Thread
的类声明:segmentfault
咱们能够知道 Thread
本身也实现了 Runnable
接口,Thread
中 run
方法的实现以下(Thread
启动以后运行的就是 Thread
中的 run
方法):多线程
(target 即构造 Thread
时可传入的 Runnable
对象,不传入即为 null
—— 因此继承 Thread
重写其 run
方法也是一种建立线程的方式)ide
题外话结束 }this
Runnable.java 的代码:spa
Runnable
的 run
方法是不带返回值的,那若是咱们须要一个耗时任务在执行完以后给予返回值,应该怎么作呢?线程
第一种方法:在 Runnable
的实现类中设置一个变量 V
,在 run
方法中将其改变为咱们期待的结果,而后经过一个 getV()
方法将这个变量返回。3d
import java.util.*; public class RunnableTest { public static void main(String[] args) throws Exception { System.out.println("使用 Runnable 得到返回结果:"); List<Thread> workers = new ArrayList<>(10); List<AccumRunnable> tasks = new ArrayList<>(10); // 新建 10 个线程,每一个线程分别负责累加 1~10, 11~20, ..., 91~100 for (int i = 0; i < 10; i++) { AccumRunnable task = new AccumRunnable(i * 10 + 1, (i + 1) * 10); Thread worker = new Thread(task, "慢速累加器线程" + i); tasks.add(task); workers.add(worker); worker.start(); } int total = 0; for (int i = 0, s = workers.size(); i < s; i++) { workers.get(i).join(); // 等待线程执行完毕 total += tasks.get(i).getResult(); } System.out.println("\n累加的结果: " + total); } static final class AccumRunnable implements Runnable { private final int begin; private final int end; private int result; public AccumRunnable(int begin, int end) { this.begin = begin; this.end = end; } @Override public void run() { result = 0; try { for (int i = begin; i <= end; i++) { result += i; Thread.sleep(100); } } catch (InterruptedException ex) { ex.printStackTrace(System.err); } System.out.printf("(%s) - 运行结束,结果为 %d\n", Thread.currentThread().getName(), result); } public int getResult() { return result; } } }
运行结果:code
第二种方法:使用 Callable<V>
和 FutureTask<V>
。 Callable<V>
是 JDK1.5 时添加的类,为的就是解决 Runnable
的痛点(没有返回值和不能抛出异常)。
Callable.java 的代码:
能够看到参数化类型 V
就是返回的值的类型。
可是查看 Thread
的构造方法,咱们发现 Thread
只提供了将 Runnable
做为参数的构造方法,并无使用 Callable<V>
的构造方法 —— 因此引出 FutureTask<V>
。
FutureTask<V>
也是 JDK1.5 时添加的类,查看它的类声明:
能够看到它实现了 RunnableFuture<V>
这个接口,咱们再看看 RunnableFuture<V>
:
能够看到 RunnableFuture
接口继承了 Runnable
接口,那么 RunnableFuture
接口的实现类 FutureTask
必然会去实现 Runnable
接口 —— 因此 FutureTask
能够用来当 Runnable
使用。查看 FutureTask
的构造方法,发现 FutureTask
有两个构造方法:
第一个构造方法代表咱们能够经过一个 Callable
去构造一个 FutureTask
—— 而 FutureTask
实现了 Runnable
接口,从而能够将该任务传递给 Thread
去运行;
第二个构造方法是经过一个 Runnable
和一个指定的 result 去构造 FutureTask
。
咱们再来看看 FutureTask<V>
经过 RunnableFuture<V>
实现的第二个接口:Future<V>
。
(事实上,RunnableFuture<V>
是在 JDK1.6 时添加的类,我猜想在 JDK1.5 时 FutureTask<V>
应该是直接实现的 Runnable
和 Future<V>
,而不是经过 RunnableFuture<V>
)
Future.java 的源码:
Future<V>
包含的方法有 5 个,本文只关注它两个 get
相关的方法。经过 Java 的 API 文档,咱们能够知道 get
方法是用来返回和 Future
关联的任务的结果(即构造 FutureTask<V>
时使用的 Callable<V>
的结果)。带参数的 get
方法指定一个超时时间,在超时时间内该方法会阻塞当前线程,直到得到结果以后中止阻塞继续运行 —— 若是在给定的超时时间内没有得到结果,那么便抛出 TimeoutException
异常;不带参数的 get
能够理解为超时时间无限大,即一直等待直到得到结果。
(Future
每一个方法的详细用法能够参考 Java 多线程(3))
经过以上咱们能够知道,Callable<V>
、Future<V>
、FutureTask<V>
这三个类是相辅相成的。以上提到关键类的类关系以下:
如今咱们看看 FutureTask
中实现 Runnable
的 run
方法是怎样的(咱们直接看关键代码):
代码的意思很明确,调用构造 FutureTask<V>
时传入的 Callable<V>
的 call
方法,若是正常执行完毕,那么经过 set(result)
设置结果,经过 get()
方法获得的即为这个结果 —— 思路和前面第一种方法是一致的(固然 FutureTask<V>
是更强大和更通用的类);不然即为抛出异常的状况。
使用 FutureTask<V>
的过程以下:
(1)经过一个 Callable<V>
任务或者一个 Runnable
(一开始就指定 result)任务构造 FutureTask<V>
;
(2)将 FutureTask<V>
交给 Thread
去运行;
(3)使用 FutureTask<V>
的 get
方法(或者 Thread
的 join
方法)阻塞当前线程直到得到任务的结果。
如今咱们使用 Callable
改写程序:
import java.util.*; import java.util.concurrent.*; public class CallableTest { public static void main(String[] args) throws Exception { System.out.println("使用 Callable 得到返回结果:"); List<FutureTask<Integer>> futureTasks = new ArrayList<>(10); // 新建 10 个线程,每一个线程分别负责累加 1~10, 11~20, ..., 91~100 for (int i = 0; i < 10; i++) { AccumCallable task = new AccumCallable(i * 10 + 1, (i + 1) * 10); FutureTask<Integer> futureTask = new FutureTask<>(task); futureTasks.add(futureTask); Thread worker = new Thread(futureTask, "慢速累加器线程" + i); worker.start(); } int total = 0; for (FutureTask<Integer> futureTask : futureTasks) { total += futureTask.get(); // get() 方法会阻塞直到得到结果 } System.out.println("累加的结果: " + total); } static final class AccumCallable implements Callable<Integer> { private final int begin; private final int end; public AccumCallable(int begin, int end) { this.begin = begin; this.end = end; } @Override public Integer call() throws Exception { int result = 0; for (int i = begin; i <= end; i++) { result += i; Thread.sleep(100); } System.out.printf("(%s) - 运行结束,结果为 %d\n", Thread.currentThread().getName(), result); return result; } } }
运行结果:
能够看到使用 Callable<V>
+ FutureTask<V>
的程序代码要比 Runnable
的代码更简洁和方便 —— 当须要线程执行完成返回结果时(或者任务须要抛出异常),Callable<V>
是优先于 Runnable
的选择。