Runnable的缺陷

  • 不能返回一个返回值
  • 不能抛出checked Exception
  • 因为run使用void修饰的也不能抛出异常

Callable接口

  • 类似于Runnable,被其他线程执行的任务
  • 实现call方法(类似于实现run方法)
  • 是有返回值的也可以抛出异常

Future类

  • 作用:一个方法可能会很耗时,就可以用一个子线程来执行这个耗时的方法,就不用一直等,直到想要获取结果时就可以通过Future来控制
  • Future和Callable的关系:
    • 可以通过Future的get方法获取Callable的执行结果
    • 可以通过Future的isDown判断任务是否执行完毕
    • 还有取消等方法
    • 在call()未执行完毕之前,调用get()的线程就会被阻塞,直到call()方法返回了结果,线程才能获取结果并且切换到runnable状态
    • Future是一个存储器,它存储了call()任务的结果,但是这个任务的执行时间是无法提前确定的,所以这取决于call()方法执行的情况
Future的主要方法
  • get():get方法的行为取决于Callable任务的状态

    • 任务正常完成:get方法会立刻返回结果
    • 任务没有完成:get将阻塞并直到任务完成
    • 任务执行过程中抛出异常:get方法会抛出ExecutionException,如果已经发生异常,但是没有get也不会抛出
    • 任务被取消:get方法会抛出CancellationException
    • 任务超时:会抛出TimeoutException
  • get(long timeout,TimeUnit unit):设置一个超时时间的获取,如果超时了会抛出TimeoutException

  • cancel:取消任务的执行,可以传入true/false,代表是否中断正在执行的任务

    • 如果任务还没有开始执行,任务正常取消,返回true
    • 如果任务已取消或者已完成,那么会执行失败,返回false
    • 如果任务在执行,就不会直接取消该任务,会根据填写的参数判断
    • 当知道任务能正确处理Interrupt的时候才传入true
    • 如果未能处理interrupt、不清楚任务是否支持取消、需要等待已经开始的任务执行完成就需要传入false
  • isDone:判断线程是否执行完毕(执行完毕不一定是执行成功)

  • isCancelled:判断是否被取消

代码示例get

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
Future<Integer> future = service.submit(new CallableTask());
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
service.shutdown();
}

static class CallableTask implements Callable {
@Override
public Integer call() throws Exception {
Thread.sleep(3 * 1000);
return new Random().nextInt();
}
}

演示批量获取结果

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
public static void main(String[] args) {

ExecutorService service = Executors.newFixedThreadPool(3);
ArrayList<Future> futures = new ArrayList<>();
for (int i = 0; i < 20; i++) {
//lambda表达式的方式
Callable<Integer> callable = ()->{
Thread.sleep(2 * 1000);
return new Random().nextInt();
};
Future<Integer> future = service.submit(callable);
futures.add(future);
}

for (int i = 0; i < 20; i++) {
Future<Integer> future = futures.get(i);
try {
Integer result = future.get();
System.out.println(result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
service.shutdown();
}

演示延时get以及cancel

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
public static final Info DEFAULT = new Info("无消息");

private static final ExecutorService service = Executors.newFixedThreadPool(10);

static class Info {
String name;

public Info(String name) {
this.name = name;
}

@Override
public String toString() {
return "info{" +
"name='" + name + '\'' +
'}';
}
}

static class GetInfoTask implements Callable<Info> {
@Override
public Info call() throws Exception {
try {
Thread.sleep(3 * 1000);
} catch (InterruptedException e) {
System.out.println("睡眠是被中断了");
}
return new Info("消息-----");
}
}

public void printInfo() {
Future<Info> future = service.submit(new GetInfoTask());
Info info;
try {
info = future.get(2 * 1000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
info = new Info("被中断时的消息===");
} catch (ExecutionException e) {
info = new Info("异常时的消息===");
} catch (TimeoutException e) {
info = new Info("超时的消息===");
System.out.println("超时");
boolean cancel = future.cancel(true);
System.out.println("cancel->" + cancel);
}
service.shutdown();
System.out.println(info);
}

public static void main(String[] args) {
new FutrueTimeout().printInfo();
}
FutureTask
  • FutureTask来获取Future任务结果
  • FutureTask是一种包装器,可以把Callable转化成Future和Runnable,他同时实现二者的接口
  • 它既可以作为Runnable被线程执行,又可以作为Future获取Callable的返回值

代码演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class FutureTaskDemo {
public static void main(String[] args) {
Task task = new Task();
FutureTask<Integer> futureTask = new FutureTask<>(task);
new Thread(futureTask).start();

try {
System.out.println(futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}

class Task implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("子线程在执行");
Thread.sleep(3 * 1000);
return 20;
}
}

注意点

  • 当循环获取future结果时,容易发生部分线程执行很慢的情况,get方法调用时应使用延时获取
  • Future的生命周期不能后退,比如线程结束后就不能变成new或者runnable状态