下面代码,多线程内代码执行会各自执行,但当对future利用get获取结果时,会被get阻塞,阻塞顺序按照executor的提交顺序:c2/c3/c1。即首先get到c2的结果,这之中休眠3秒;然后get到c3的结果,这之中休眠1秒,最后get到c1的结果,这之中休眠2秒。get在主线程中完全阻塞住了多线程获取结果的时间。
ExecutorService es = Executors.newFixedThreadPool(3);
Callable<Integer> c1 = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Thread.sleep(2000);
return 11;
}
};
Callable<Integer> c2 = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Thread.sleep(3000);
return 22;
}
};
Callable<Integer> c3 = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Thread.sleep(1000);
return 33;
}
};
ArrayList<Callable> cs = new ArrayList<>();
cs.add(c2);
cs.add(c3);
cs.add(c1);
for (Callable c : cs) {
Future<Integer> f = es.submit(c);
try {
System.out.println(f.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
es.shutdown();
利用ExecutorCompletionService的tack方法,可以按照线程各自执行完成的顺序,进行结果输出。如下面代码,输出顺序为:c3/c1/c2,这是因为,ExecutorCompletionService的tack方法不会在主线程阻塞,虽然提交顺序是c2/c3/c1,但会统筹所有线程休眠时间,按照时间依次执行,即:以项目启动时间为起点,1秒后执行c3,2秒后执行c1,3秒后执行c2,三个线程任务执行获取结果的时间间隔为1秒。
ExecutorService es = Executors.newFixedThreadPool(3);
ExecutorCompletionService<Integer> ecs = new ExecutorCompletionService<>(es);
Callable<Integer> c1 = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Thread.sleep(2000);
return 11;
}
};
Callable<Integer> c2 = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Thread.sleep(3000);
return 22;
}
};
Callable<Integer> c3 = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Thread.sleep(1000);
return 33;
}
};
ArrayList<Callable> cs = new ArrayList<>();
cs.add(c2);
cs.add(c3);
cs.add(c1);
for (Callable c : cs) {
ecs.submit(c);
}
for (int n = 0; n < cs.size(); n++) {//注意这里不用future去get,而是直接用ExecutorCompletionService的take结果去get,get的次数要与线程数一致,利用Callable的集合取确定线程数
try {
Integer i = ecs.take().get();//take:有就返回,没有就等到有再返回;poll无参:直接返回,没执行出结果则直接返回null;poll有参:有就返回,没有就等到入参定义的时间后返回执行结果,若还是没有执行到,则直接返回null
System.out.println(i);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
es.shutdown();
在读下面代码前,先区分下线程池的几种关闭方式:
1、shutdown方法:将线程池状态置为SHUTDOWN。平滑的关闭ExecutorService,当此方法被调用时,ExecutorService停止接收新的任务并且等待已经提交的任务(包含提交正在执行和提交未执行)执行完成。当所有提交任务执行完毕,线程池即被关闭。
2、awaitTermination方法:接收人timeout和TimeUnit两个参数,用于设定超时时间及单位。当等待超过设定时间时,会监测ExecutorService是否已经关闭,若关闭则返回true,否则返回false。一般情况下会和shutdown方法组合使用。
3、shutdownNow方法:将线程池状态置为STOP。跟shutdown()一样,先停止接收外部提交的任务,忽略队列里等待的任务,尝试将正在跑的任务interrupt中断,返回未执行的任务列表。
public class TestDemo2 {
private static final int TIMES = 60;
public static void main(String[] args) throws Exception {
Callable<Integer> cal1 = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Thread.sleep(10000);
return 11;
}
};
Callable<Integer> cal2 = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return 22;
}
};
Callable<Integer> cal3 = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Thread.sleep(7000);
return 33;
}
};
Callable<Integer> cal4 = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return 44;
}
};
Callable<Integer> cal5 = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
return 55;
}
};
ExecutorService es = Executors.newFixedThreadPool(5);
ExecutorCompletionService<Integer> service = new ExecutorCompletionService<>(es);
ArrayList<Callable> list = new ArrayList<>();
list.add(cal2);
list.add(cal3);
list.add(cal4);
list.add(cal5);
list.add(cal1);
for (Callable c : list) {
service.submit(c);
}
es.shutdown();//这里就是只有当线程池没有了正在执行的线程,且任务队列内没有任务后,可平滑关闭。
int times = TIMES;
while (true) {
try {
if (es.awaitTermination(1, TimeUnit.SECONDS)||times<=0){//此处表示,每秒检测下原线程池是否平滑关闭,或者当前是否满60秒了,满足条件,则跳到下面输出结果
break;
}
times--;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int i = 0; i < 5; i++) {
System.out.println(service.poll().get());
}
}
}
最终,上面代码将在10秒后把结果按照各线程休眠时间确定执行顺序,然后直接一起输出:22、55、44、33、11。