Runnable的缺陷
有两大缺陷;
1.不能返回一个返回值,返回值为void
2.也不能抛出checked Exception
在run()方法中,无法抛出异常,只能使用try catch 来处理异常;下面的那个对比方法是可以抛出异常的;
为什么又这样的缺陷?
因为它设计的时候,方法返回值为void 而且也设计成无法抛出异常;
Runnable 为什么设计成这样?
如果抛出异常后,就给Thread 这时候没办法处理;所以,就设计成为直接在Runnable中进行处理;
针对于无法抛出检查后异常这个缺陷的补救措施就是Callable接口
Callable接口
源码如下:
可以看到第一它又返回值,返回值是泛型;第二,它可以抛出异常;
Future类
Future作用:核心思想,一个方法它的计算可能会很耗时,那么它就会让子线程去执行,然后再去干自己的事情,等他想用这个返回值的时候,再去看他有没有计算出来;如果计算出结果了,就去获取这个值;
Future和Callable的关系
Future的主要方法 有五个
取消的方法
完毕不一定是成功执行,失败了,执行完毕也会返回true;
代码演示:
package future;
import java.util.Random;
import java.util.concurrent.*;
/**
* 描述 : 演示一个Future的使用方法
*/
public class OneFuture {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
Future<Integer> future = service.submit(new CallableTask());
try {
// 通过get来获取值,这里需要等待大概3秒钟时间
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
service.shutdown();
}
static class CallableTask implements Callable<Integer>{
@Override
public Integer call() throws Exception {
Thread.sleep(3000);
return new Random().nextInt(1000);
}
}
}
结果会是一个随机数;
下面进行lambda表达式,会方便一些;
package future;
import java.util.Random;
import java.util.concurrent.*;
/**
* 描述 : 演示一个Future的使用方法,lambda表达式
*/
public class OneFuture {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
Future<Integer> future = service.submit(new Callable() {
@Override
public Integer call() throws Exception {
Thread.sleep(3000);
return new Random().nextInt(1000);
}
});
try {
// 通过get来获取值,这里需要等待大概3秒钟时间
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
service.shutdown();
}
}
多个任务,用Future数组来获取结果;
package future;
import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.*;
/**
* 描述: 演示批量提交任务时,用List来批量接收结果;
*/
public class MultiFutures {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(2);
ArrayList<Future> futures = new ArrayList<>();
// 提交任务,向数组中提交20个future
for (int i = 0; i < 20; i++) {
Future<Integer> future = service.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Thread.sleep(3000);
return new Random().nextInt(1000);
}
});
futures.add(future);
}
// 提交完任务以后,下面的任务还会继续执行
for (int i = 0; i < 20; i++) {
Future<Integer> future = futures.get(i);
try {
// 注意程序会执行到这里,当这里获取不到的时候,才会进行阻塞
Integer integer = future.get();
System.out.println(integer);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
结果是,没3秒中,会打印两个
任务执行过程中抛出Exception和isDone展示:
package future;
import java.util.concurrent.*;
/**
* 描述: 演示get方法过程中抛出异常,for循环为了演示抛出Exception的时机:并不是说一产生异常就抛出,
* 直到我们get执行时,才会抛出
*/
public class GetException {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(20);
Future<Integer> future = service.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
throw new IllegalAccessException("Callable抛出异常");
}
});
try {
future.get();
} catch (InterruptedException e) {
System.out.println("抛出了InterruptedException异常");
e.printStackTrace();
} catch (ExecutionException e) {
System.out.println("抛出了ExecutionException异常");
e.printStackTrace();
}
}
}
结论如下:
这里虽然抛出的IllegalAccessException(“Callable抛出异常”),但是,其抛出的都是ExecutionException。
对其升级:
package future;
import java.util.concurrent.*;
/**
* 描述: 演示get方法过程中抛出异常,for循环为了演示抛出Exception的时机:并不是说一产生异常就抛出,
* 直到我们get执行时,才会抛出
*/
public class GetException {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(20);
Future<Integer> future = service.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
throw new IllegalAccessException("Callable抛出异常");
}
});
try {
// 其实任务一在线程池中,它就已经是存在了,但是你不会调用get,它是不会抛出的
for (int i = 0; i < 5; i++) {
System.out.println(i);
Thread.sleep(500);
}
future.get();
} catch (InterruptedException e) {
System.out.println("抛出了InterruptedException异常");
e.printStackTrace();
} catch (ExecutionException e) {
System.out.println("抛出了ExecutionException异常");
e.printStackTrace();
}
}
}
可以证明,只有在调用get的时候,才会抛出异常;
isDone()方法测试
获取任务超时学习:
package future;
import java.util.concurrent.*;
/**
* 描述: 演示get的超时方法,需要注意超时后,需要处理,调用future.cancel();
* 演示cancel传入true和false的区别,
* 代表是否中断正在执行的任务。
*/
public class Timeout {
private static final Ad DEFAULT_AD = new Ad("无网络时候的默认广告");
private static final ExecutorService exec = Executors.newFixedThreadPool(10);
static class Ad{
String name;
public Ad(String name){
this.name = name;
}
@Override
public String toString(){
return "Ad{" + "name =" + name + "}";
}
}
static class FetchAdTask implements Callable<Ad> {
@Override
public Ad call() throws Exception {
try {
Thread.sleep(3000);
}catch (InterruptedException e){
System.out.println("sleep期间被中断了");
return new Ad("被中断时候的默认广告-1");
}
return new Ad("有网络的广告");
}
}
public void printAd(){
Future<Ad> f = exec.submit(new FetchAdTask());
Ad ad;
try {
ad = f.get(4000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
ad = new Ad("被中断时候的默认广告");
e.printStackTrace();
} catch (ExecutionException e) {
ad = new Ad("被中断时候的默认广告");
e.printStackTrace();
} catch (TimeoutException e) {
ad = new Ad("被中断时候的默认广告");
System.out.println("超时,未获取到广告");
boolean cancel = f.cancel(false);
System.out.println("cancel的结果:" + cancel);
}
exec.shutdown();
System.out.println(ad);
}
public static void main(String[] args) {
Timeout timeout = new Timeout();
timeout.printAd();
}
}
首先,他会去执行printAd()方法,然后,就执行上面的函数,f.get()方法会去获取,在4s内,如果有返回值的话,它就会获取到,而FetchAdTask()任务中,过个3s后,会返回一个有网络的广告;
如果,我把这个时间改为2000,那么他就会产生如下结果:
这时候,会放弃结果;
但是,当我们f.cancel()中传入true时,这个时候,当一定时间内没获取到的时候,它就会抛出一个异常;
它会在call()运行的时候,抛出一个异常;就是在Thread.sleep(3000);的过程抛出异常;
如果填入的是ture,那么就给他一个中断信号,如果填入的是false,那么就不会有中断信号,会继续执行下去;
如果我们知道该任务可以处理中断,那么就可以调用cancel(true)来处理;
其他的条件一般用false 它也是可以来避免启动尚未启动的任务的;
用FutureTask来创建Future 以前是使用submit()的返回来获得Future的,现在可以使用FuturnTask来获取Future;
代码演示:
package future;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* 描述: 演示FutureTask的用法
*/
public class FutureTaskDemo {
public static void main(String[] args) {
Task task = new Task();
FutureTask<Integer> integerFutureTask = new FutureTask<Integer>(task);
// FutureTask继承Runnable接口,所以,可以这么使用;
new Thread(integerFutureTask).start();
try {
// 使用FutureTask可以把结果进行返回;
System.out.println("task运行结果:" + integerFutureTask.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(3000);
int sum = 0;
for (int i = 0; i < 100; i++) {
sum = sum + 1;
}
return sum;
}
}
下面是用线程池来使用;
package future;
import java.util.concurrent.*;
/**
* 描述: 演示FutureTask的用法
*/
public class FutureTaskDemo {
public static void main(String[] args) {
Task task = new Task();
FutureTask<Integer> integerFutureTask = new FutureTask<Integer>(task);
/*// FutureTask继承Runnable接口,所以,可以这么使用;
new Thread(integerFutureTask).start();*/
ExecutorService service = Executors.newFixedThreadPool(10);
// 把它放到线程池中进行执行
service.submit(integerFutureTask);
try {
// 使用FutureTask可以把结果进行返回; 也可以来进行调用
System.out.println("task运行结果:" + integerFutureTask.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(3000);
int sum = 0;
for (int i = 0; i < 100; i++) {
sum = sum + 1;
}
return sum;
}
}
结果如下:
比如说:你获取的第一个future非常慢,那么你后面的future就会被阻塞;