前言:我们一般通过继承Thread类重写run方法或者实现runnable接口重写run方法,最后创建和启动一个线程,但是都需要自己创建、启动Thread对象。线程池可以实现帮助我们管理Thread对象,至于要使用几个线程,什么时候启动这些线程,是开启多个线程还是用单个线程来完成这些任务,我们无需操心。
Java通过Executors提供四种线程池
- newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。(线程最大并发数不可控制)
- newFixedThreadPool:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
- newScheduledThreadPool:创建一个定长线程池,支持定时及周期性任务执行。
- newSingleThreadExecutor:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
package threadTest.executors;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @ClassName CachedThreadPool
* @Description TODO
* @Author Kikityer
* @Date 2018/11/21 19:04
* @Version 1.0.0
**/
public class CachedThreadPool {
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
for (int i = 0;i < 5; i++){
exec.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" is doing task");
}
});
}
exec.shutdown();
}
}
//结果:
/**
*pool-1-thread-1 is doing task
*pool-1-thread-2 is doing task
*pool-1-thread-3 is doing task
*pool-1-thread-4 is doing task
*pool-1-thread-5 is doing task
*/
通过for循环建立了5个任务,通过submit方法开启了五个线程处理这五个任务。
submit有两个常用的方法:
Future<?> submit(Runnable task) 接收Runnable类型入参,而Runnable无返回值 。
<T> Future<T> submit(Callable<T> task) 接收Callable类型入参 ,Callable入参允许任务返回值。
package threadTest.executors;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
/**
* @ClassName CallableAndFuture
* @Description TODO
* @Author Kikityer
* @Date 2018/11/21 19:20
* @Version 1.0.0
**/
public class CallableAndFuture {
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
List<Callable<String>> taskList = new ArrayList<>(); //任务列表,用于存放任务
for (int i = 0; i < 5; i++){
taskList.add(new MyCallable()); //创建5个任务,并且添加到列表中
}
List<Future<String>> resultList = new ArrayList<>(); //结果列表,用于存放任务执行的结果
try {
resultList = exec.invokeAll(taskList); //invokeAll是等所有任务完成后返回代表结果的Future列表
exec.shutdown();
} catch (InterruptedException e) {
e.printStackTrace();
}
for (Future<String> future : resultList){ //遍历结果列表,打印出每个任务的结果,即打印出每个当前线程的名字(String类型)
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
/**
* MyCallable实现Callable接口,创建一个可以有返回值的任务类
*/
class MyCallable implements Callable<String>{
@Override
public String call() {
return Thread.currentThread().getName();
}
}
//结果:
/**
*pool-1-thread-1
*pool-1-thread-2
*pool-1-thread-3
*pool-1-thread-4
*pool-1-thread-5
*/
在上面的代码中,我们创建了一个类实现了一个Callable接口,此类是一个有返回值的任务类。而后开启了一个线程池用来管理线程。从结果可以看出我们得到了任务类所返回的信息。其中用到了Future接口、invokeAll方法、get方法。
Future接口
Future 表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。上面的例子中exec执行器执行了一个Callable类型的任务列表然后得到了Futuer类型的结果列表resultList。
invokeAll方法
invokeAll批量运行所有任务。和submit的区别是:submit提交单个任务
get方法
等待计算完成,获取其结果。get方法会阻塞直到结果返回。
FutureTask
FutureTask类是 Future 接口的一个实现。FutureTask类实现了RunnableFuture接口,RunnableFuture继承了Runnable接口和Future接口所以:
可以看出FutureTask可以当作一个有返回值的Runnable任务来用。
- FutureTask可以作为Runnable被线程执行
- 可以作为Future得到传入的Callable对象的返回值 如:futureTask.get()