创建线程有哪几种方式
- 继承Thread类(Thread类也是实现Runnable接口);
public
class Thread implements Runnable {
- 实现Runnable接口;
- 通过线程池创建线程池;
- 实现Callable接口与ExecutorService结合使用
因为Java的类是单继承,接口可以多实现。所以在创建子任务的时候,更多的是选择实现接口。线程池的出现为了让线程可以很好的复用,减少系统开销(线程的启动、销毁等过程是比较复杂的),同时也统一把线程管理起来。
Runnable不足?
现Runnable接口中的run()方法:
public abstract void run();
通过源码中的run方法可以看出,run方法没有返回值。如果我们需要接受子线程返回结果,此时该接口已经不满足我们的需求了。那么怎么接受子线程返回的结果呢?此时就需要通过Callable接口创建线程了。同样的先看看过Callable接口中的方法定义
Callable接口中的call()方法
V call() throws Exception;
和run()对比,可以看出该方法支持返回值,支持向上抛出异常。
Callable接口的简单实例
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newCachedThreadPool();
Future<String> future = executorService.submit(new Task());//submit结果
String result = future.get();//取出子线程运行结果
System.out.println("子任务的执行结果:"+result);//子任务的执行结果:1995-01-01
executorService.shutdown();
}
private static class Task implements Callable<String> {
@Override
public String call() throws Exception {
return "1995-01-01";
}
}
FutureTask获取结果
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newCachedThreadPool();
//通过Callable实例构建FutureTask实例
FutureTask<String> futureTask = new FutureTask<>(new Task());
//提交任务到线程池
executorService.execute(futureTask);
//取出子线程运行结果
String result = futureTask.get();
System.out.println("子任务的执行结果:" + result);//子任务的执行结果:1995-01-02
}
private static class Task implements Callable<String> {
@Override
public String call() throws Exception {
return "1995-01-02";
}
}
Future接口中方法
其实根据命名,已经可以推测出这些方法的作用了,下面主要记录一些注意点
- get():获取Callable接口返回的结果,假设主线程调用了get()方法,但是此时子线程还没有执行完毕(即还没有返回结果),此时主线程会被阻塞,知道call()方法返回了结果。
- get(long timeout, TimeUnit unit):在get()的基础上加上超时时间;
- isDone():判断子线程是否执行完毕;
- cancel(boolean mayInterruptIfRunning):取消线程
取消线程坑时可能遇到的几种情况
1.线程还没有执行:返回true
2.线程已经执行完毕或者取消了,返回false;
3.线程已经开始执行了,此时根据设置的mayInterruptIfRunning来判断是否直接取消任务。mayInterruptIfRunning为ture适用于子任务有逻辑处理中断。false适用于子线程没有能力处理中断;需求是需要等待已经开始的线程执行完毕。
FutureTask
FutureTask:实现了Runnable和Future接口,表示异步计算的结果。