Callable和Runnable的区别
- Callable接口的call()方法可以有返回值(通过Future接口进行获得的),而Runnable接口的run()方法没有返回值。
- Callable接口的call()方法可以声明抛出异常,而Runnable接口的run()方法不可以声明抛出异常。
Future的使用
作为多线程的异步执行结果,Future是Callable的返回值,需要结合ExecutorService中的submit(Callable)
其get()用于获得返回值,但是具有阻塞性,isDone()不会阻塞
//可以抛出异常,可以有返回值
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(1000);
return "i am callable";
}
};
System.out.println(System.currentTimeMillis()+"开始了===");
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(4, 9, 0L, TimeUnit.SECONDS, new LinkedBlockingDeque());
Future<String> future=threadPoolExecutor.submit(callable);
try {
System.out.println("验证isDone是否阻塞性==="+future.isDone()+"==="+System.currentTimeMillis());
System.out.println(future.get()+"==="+System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis()+"结束了===");
结果
ExecutorService中的submit()方法也是可以传递Runable的,并且返回的也是Future,也是通过get()获取返回值,但是因为Runnable接口不具备返回值特性,所以get返回为null,同时get依旧具有阻塞性。
Runnable runnable1=new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
System.out.println(System.currentTimeMillis()+"开始了===");
ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(4,9,0L,TimeUnit.SECONDS,new LinkedBlockingDeque<>());
Future future=threadPoolExecutor.submit(runnable1);
try {
System.out.println(future.get()+"==="+System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis()+"结束了===");
结果
ExecutorService中的submit()方法传递Runable,通过Future.get()获取的返回值是null,但是可以通过传入一个载体去将处理后的结果赋值给这个载体进而得到返回值。
此时可以用Future的get()方法来执行,得到载体的值,
如果不用get()方法的话,可以休息足够的时间(线程执行完)最后也能取到载体的值
Future的get()方法来执行
public class model {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "model{" +
"name='" + name + '\'' +
'}';
}
}
model model1 = new model();
System.out.println(System.currentTimeMillis() + "开始了===");
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(4, 9, 0L, TimeUnit.SECONDS, new LinkedBlockingDeque<>());
Future<model> future=threadPoolExecutor.submit(()->{
try {
model1.setName("我是Ruanble的载体");
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
},model1);
try {
model model2=future.get();
System.out.println("model===" + model2+ "===" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis() + "结束了===");
结果
如果不用get,等线程执行完也能获取到载体的值(不建议这种做法,因为无法保证执行时间)
model model1 = new model();
System.out.println(System.currentTimeMillis() + "开始了===");
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(4, 9, 0L, TimeUnit.SECONDS, new LinkedBlockingDeque<>());
threadPoolExecutor.submit(()->{
try {
model1.setName("我是Ruanble的载体");
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
},model1);
try {
TimeUnit.SECONDS.sleep(3);
System.out.println("model===" + model1+ "===" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(System.currentTimeMillis() + "结束了===");
结果
在多个Future存在情况下,各个Future执行完的时间长短不一,无法保证先执行完的Future先返回,是Future的缺点
public class JucThread03 implements Callable<String> {
private String name;
private Integer sleeps;
public JucThread03(String name, Integer sleep) {
this.name = name;
this.sleeps = sleep;
}
@Override
public String call() throws Exception {
Thread.sleep(sleeps);
return name;
}
}
Callable callable1 = new JucThread03("001", 2000);
Callable callable2 = new JucThread03("002", 4000);
Callable callable3 = new JucThread03("003", 5000);
Callable callable4 = new JucThread03("004", 3000);
Callable callable5 = new JucThread03("005", 1000);
List<Future> futureList=new ArrayList<>();
ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(5,10,0L, TimeUnit.SECONDS,new LinkedBlockingDeque<>());
futureList.add(threadPoolExecutor.submit(callable1));
futureList.add(threadPoolExecutor.submit(callable2));
futureList.add(threadPoolExecutor.submit(callable3));
futureList.add(threadPoolExecutor.submit(callable4));
futureList.add(threadPoolExecutor.submit(callable5));
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-mm-dd HH:mm:ss");
System.out.println(sdf.format(new Date()));
try {
for(int i=0;i<5;i++){
System.out.println("name==="+futureList.get(i).get()+"时间==="+sdf.format(new Date()));
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("wanshile ");
threadPoolExecutor.shutdown();
结果
任务4和5早就执行完了,但是由于没有提前调用get()所以最后才出结果,很影响程序运行效率。
批量处理异步任务,invokeAll(),该方法也是阻塞方法,一直等待所有任务都执行完才能返回结果(先执行完的要等待后执行完的,然后一起返回结果)
System.out.println("++++++++++"+System.currentTimeMillis());
ExecutorService service = Executors.newFixedThreadPool(5);
List<Callable<Integer>> callables = new ArrayList<>();
for (int i = 0; i < 5; i++) {
callables.add(() -> {
int time = new Random().nextInt(5);
System.out.println(Thread.currentThread().getName() + "===" + time);
TimeUnit.SECONDS.sleep(time);
return time;
});
}
List<Future<Integer>> futureList=service.invokeAll(callables);
for (Future<Integer> future:futureList){
System.out.println(future.get()+"%%%"+System.currentTimeMillis());
}
System.out.println("ok"+"@@@@"+System.currentTimeMillis());
结果
Future的缺点
▪ 无法被动接收异步任务的计算结果:虽然我们可以主动将异步任务提交给线程池中的线程来执行,但是待异步任务结束后,主(当前)线程无法得到任务完成与否的通知,它需要通过get方法主动获取计算结果。
▪ 多个Future存在时无法保证先执行完的先返回:程序运行中多个任务,如果前面的任务耗时时间很长,后面的耗时很短,但由于不知道谁先执行完毕,进而循环调用get()时,则会造成先执行完的反而耗时等待,影响后续的执行效率。
▪ Future间彼此孤立:有时某一个耗时很长的异步任务执行结束以后,你还想利用它返回的结果再做进一步的运算,该运算也会是一个异步任务,两者之间的关系需要程序开发人员手动进行绑定赋予,Future并不能将其形成一个任务流(pipeline),每一个Future彼此之间都是孤立的。
▪ Future没有很好的错误处理机制:截至目前,如果某个异步任务在执行的过程中发生了异常错误,调用者无法被动获知,必须通过捕获get方法的异常才能知道异步任务是否出现了错误,从而再做进一步的处理。
ExecutorService方法execute()与submit()的区别
- 方法execute()没有返回值,而submit()方法可以有返回值
- 方法execute()在默认的情况下异常直接抛出,不能捕获,但可以通过自定义Thread-Factory的方式进行捕获,而submit()方法在默认的情况下,可以catchExecution-Exception捕获异常。
execute()无法捕获异常
try {
Executors.newFixedThreadPool(2).execute(() -> {
Integer.parseInt("a");
});
} catch (Exception e) {
e.printStackTrace();
System.out.println("捕获异常===" + e.getMessage());
}
System.out.println("继续");
结果
submit()捕获异常
try {
Future future = Executors.newFixedThreadPool(2).submit(() -> {
return Integer.parseInt("a");
});
future.get();
} catch (Exception e) {
e.printStackTrace();
System.out.println("捕获异常===" + e.getMessage());
}
System.out.println("继续");
结果