在Java 5之后,任务分两类:一类是实现了Runnable接口的类,一类是实现了Callable接口的类。两者都可以被ExecutorService执行,但是Runnable任务没有返回值,而Callable任务有返回值。并且Callable的call()方法只能通过ExecutorService的submit(Callable<T> task) 方法来执行,并且返回一个 <T>Future<T>,是表示任务等待完成的 Future。
Callable接口类似于Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。但是 Runnable 不会返回结果,并且无法抛出经过检查的异常而Callable又返回结果,而且当获取返回结果时可能会抛出异常。Callable中的call()方法类似Runnable的run()方法,区别同样是有返回值,后者没有。
当将一个Callable的对象传递给ExecutorService的submit方法,则该call方法自动在一个线程上执行,并且会返回执行结果Future对象。同样,将Runnable的对象传递给ExecutorService的submit方法,则该run方法自动在一个线程上执行,并且会返回执行结果Future对象,但是在该Future对象上调用get方法,将返回null。
static class CalcTask implements Callable<List<KeywordCategoryRelated>>{
private List<KeywordCategoryRelated> keyCateList;
private String addressIp;
public CalcTask(String addressIp,List<KeywordCategoryRelated> keyCateList){
this.keyCateList = keyCateList;
this.addressIp = addressIp;
}
@Override
public List<KeywordCategoryRelated> call() throws Exception {
for(KeywordCategoryRelated item : keyCateList){
String imgUrl = RestRequestUtil.getProductImgUrl(item.getCategoryId(),item.getKeyword(),addressIp);
if("error".equals(imgUrl)){
// 如果超时或者报错则重试一次(正常返回不重试)
imgUrl = RestRequestUtil.getProductImgUrl(item.getCategoryId(),item.getKeyword(),addressIp);
}
item.setImgUrl(imgUrl);
}
return keyCateList;
}
}
ExecutorService es = Executors.newFixedThreadPool(ThreadNum);
CompletionService<List<KeywordCategoryRelated>> completionService = new ExecutorCompletionService<List<KeywordCategoryRelated>>(es);
try {
for(List<KeywordCategoryRelated> item : categoryRelatedTempSet){
String addressIp = addressList.get(countIp % addressList.size());
countIp++;
CalcTask calcTask = new CalcTask(addressIp, item);
completionService.submit(calcTask);
logger.info("submit " + num + " success");
}
for(List<KeywordCategoryRelated> item : categoryRelatedTempSet){
Future<List<KeywordCategoryRelated>> feature = completionService.take();
List<KeywordCategoryRelated> categoryRelatedTempList = feature.get();
result.addAll(categoryRelatedTempList);
logger.info("get " + num + " success");
}
} catch (Exception e) {
es.shutdown();
logger.info("获取图片多线程失败,计算已退出...");
e.printStackTrace();
return false;
}
es.shutdown();
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
public class Main {
static class Calcuete implements Callable<String>{
private String name;
public Calcuete(String name){
this.name = name;
}
@Override
public String call() throws Exception {
return name;
}
}
public static void main(String[] args) {
List<String> nameList = new ArrayList<String>();
for(int i = 0;i<10;i++){
nameList.add("azure" + i);
}
int thredNum = nameList.size();
/**********ExecutorService提交**********/
ExecutorService es1 = Executors.newFixedThreadPool(thredNum);
List<Future<String>> futureList = new ArrayList<Future<String>>();
for(String name : nameList) {
Calcuete calcuete = new Calcuete(name);
Future<String> future = es1.submit(calcuete);
futureList.add(future);
}
while(thredNum > 0){
for(Future<String> future : futureList){
String result = null;
try {
result = future.get(0, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (TimeoutException e) {
//超时不处理
}
if(result != null){
futureList.remove(future);
thredNum--;
break;
}
}
}
/**
* 这两者最主要的区别在于submit的task不一定是按照加入自己维护的list顺序完成的。
从list中遍历的每个Future对象并不一定处于完成状态,这时调用get()方法就会被阻塞住,
如果系统是设计成每个线程完成后就能根据其结果继续做后面的事,这样对于处于list后面的
但是先完成的线程就会增加了额外的等待时间。
而CompletionService的实现是维护一个保存Future对象的BlockingQueue。只有当这个
Future对象状态是结束的时候,才会加入到这个Queue中,take()方法其实就是Producer-Consumer
中的Consumer。它会从Queue中取出Future对象,如果Queue是空的,就会阻塞在那里,
直到有完成的Future对象加入到Queue中。
所以,先完成的必定先被取出。这样就减少了不必要的等待时间
*/
/**********CompletionService提交**********/
int thredNum1 = nameList.size();
ExecutorService es = Executors.newFixedThreadPool(thredNum1);
CompletionService<String> completionService = new ExecutorCompletionService<String>(es);
for(String name : nameList) {
Calcuete calcuete = new Calcuete(name);
completionService.submit(calcuete);
Future<String> future = es.submit(calcuete);
futureList.add(future);
}
for(String temp : nameList) {
try {
Future<String> future = completionService.take();
String name = future.get();
System.out.println(name);
} catch (Exception e) {
e.printStackTrace();
es.shutdown();
}
}
es.shutdown();
}
}