Callable和Runnable
Runnable封装一个异步运行的任务,没有返回值。
- Callable与Runnable类似,但有返回值。
- Callable的call()方法可以抛出异常
- 运行Callable可以拿到Future对象
public interface Callable<V>
{
V call() throws Exception;
}
类型参数是返回值的类型。Callable< Integer>返回Integer对象
Future和FutureTask
Future接口和实现Future接口的FutureTask类用来表示异步计算的结果。
Future对象的所有者在结果计算好后可以获得它。
public interface Future<V> {
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
void cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
}
- get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
- get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,抛出TimeoutException异常。
- cancel()取消该计算。如果计算没开始,计算被取消返回true;正在运行中,若mayInterruptIfRunning为true,则被中断返回true;若已结束,返回false。
- isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。
- isDone方法表示任务是否已经完成,若任务完成,则返回true。
Future提供了三种功能:
- 判断任务是否完成;
- 能够中断任务;
- 能够获取任务执行结果。
因为Future只是一个接口,所以是无法直接用来创建对象使用的,因此就有了下面的FutureTask。
FutureTask实现了RunnableFuture接口,而RunnableFuture继承了Runnable和Future,也就是说FutureTask既是Runnable,也是Future。
另:多个线程执行一个FutureTask只会执行一次,其它属于复用,多次计算应用多个FutureTask
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
sync = new Sync(callable);
}
public FutureTask(Runnable runnable, V result) {
sync = new Sync(Executors.callable(runnable, result));
}
FutureTask可将Callable转换成Future和Runnable。
例如: 计算匹配文件数目
public class FutureTest{
public static void main(String[] args) {
try (Scanner in = new Scanner(System.in)){
System.out.print("Enter base directory (e.g. /user/local/jdk5.0/src): ");
String directory = in.nextLine();
System.out.print("Enter keyword (e.g. volatile): ");
String keyword = in.nextLine();
// 用MatchCounter创建一个FutureTask对象,并用来启动一个线程
MatchCounter counter = new MatchCounter(new File(directory), keyword);
FutureTask<Integer> task = new FutureTask<>(counter);
Thread t = new Thread(task); // 作为一个Runnable
t.start();
try{
// 每一个对get()调用都会发生阻塞直到获得结果
System.out.println(task.get() + " matching files."); // 作为一个Future
} catch (ExecutionException e) {
e.printStackTrace();
}
} catch (InterruptedException e) {
}
}
}
class MatchCounter implements Callable<Integer> {
private File directory;
private String keyword;
public MatchCounter(File directory, String keyword) {
this.directory = directory;
this.keyword = keyword;
}
public Integer call() {
int count = 0;
try {
File[] files = directory.listFiles();
List<Future<Integer>> results = new ArrayList<>();
for (File file:files) {
if (file.isDirectory()) {
// 递归,对于每个子目录都产生一个新的MatchCounter并为它启动一个线程
MatchCounter counter = new MatchCounter(file, keyword);
FutureTask<Integer> task = new FutureTask<>(counter);
// 将FutureTask对象放在ArrayList<Future<Integer>>中
results.add(task);
Thread t = new Thread(task);
t.start();
} else {
if (search(file)) count++;
}
}
for (Future<Integer> result:results) {
try {
count += result.get();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
} catch (InterruptedException e) {
}
return count;
}
// 查找给定文件中是否包含关键词
public boolean search(File file) {
try {
try (Scanner in = new Scanner(file, "UTF-8")) {
boolean found = false;
while (!found && in.hasNextLine()) {
String line = in.nextLine();
if (line.contains(keyword)) found = true;
}
return found;
} catch (IOException e) {
return false;
}
}
}
}
Callable、Future和线程池
-
Executor是一个顶层接口,在它里面只声明了一个方法execute(Runnable),返回值为void,参数为Runnable类型,从字面意思可以理解,就是用来执行传进去的任务的;
-
ExecutorService接口继承了Executor接口,并声明了一些方法:submit、invokeAll、invokeAny以及shutDown等;
- execute(task) 异步执行tasks,无返回值
- submit(task) 异步执行,且带task返回值,可通过task.get 实现同步到主线程
- invoke(task) 有join, tasks会被同步到主进程
-
抽象类AbstractExecutorService实现了ExecutorService接口,基本实现了ExecutorService中声明的所有方法;
-
类ThreadPoolExecutor继承了类AbstractExecutorService。
public ThreadPoolExecutor( int corePoolSize, //核心线程的数量
int maximumPoolSize, //最大线程数量
long keepAliveTime, //超出核心线程数量以外的线程空余存活时间
TimeUnit unit, //存活时间的单位
BlockingQueue<Runnable> workQueue, //保存待执行任务的队列
ThreadFactory threadFactory, //创建新线程使用的工厂
RejectedExecutionHandler handler // 当任务无法执行时的处理器
) {...}
可以使用两个方法向线程池提交任务,分别为execute()和submit()方法
execute()方法用于提交不需要返回值的任务(Runnable),所以无法判断任务是否被线程池执行成功。
submit()方法用于提交需要返回值的任务(Callable)。线程池会返回一个future类型的对象
例1
package multiplethread;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class TestThread {
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor threadPool= new ThreadPoolExecutor(10, 15, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
threadPool.execute(new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("任务1");
}
});
}
}
例2
public class FutureTest{
public static void main(String[] args) {
try (Scanner in = new Scanner(System.in)){
System.out.print("Enter base directory (e.g. /user/local/jdk5.0/src): ");
String directory = in.nextLine();
System.out.print("Enter keyword (e.g. volatile): ");
String keyword = in.nextLine();
ExecutorService pool = new Executors.newCachedThreadPool();
MatchCounter counter = new MatchCounter(new File(directory), keyword, pool); // 参数pool用于递归创建MatchCounter加入线程池
Future<Integer> result = pool.submit(counter);
try {
System.out.println(result.get() + " matching files.");
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
}
pool.shutdown();
// 强制转换为ThreadPoolExecutor查看线程池在执行器生命周期中的最大尺寸
int largestPoolSize = ((ThreadPoolExecutor) pool).getLargestPoolSize();
System.out.println("largest pool size = " + largestPoolSize);
}
}
}
class MatchCounter implements Callable<Integer> {
private File directory;
private String keyword;
private ExecutorService pool;
private int count;
public MatchCounter(File directory, String keyword, ExecutorService pool) {
this.directory = directory;
this.keyword = keyword;
this.pool = pool;
}
public Integer call() {
count = 0;
try {
File[] files = directory.listFiles();
List<Future<Integer>> results = new ArrayList<>();
for (File file:files) {
if (file.isDirectory()) {
// 递归,对于每个子目录都产生一个新的MatchCounter并加入线程池
MatchCounter counter = new MatchCounter(file, keyword, pool);
Future<Integer> result = pool.submit(counter);
// 保存返回的Future对象
results.add(result);
} else {
if (search(file)) count++;
}
}
for (Future<Integer> result:results) {
try {
count += result.get();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
} catch (InterruptedException e) {
}
return count;
}
// 查找给定文件中是否包含关键词
public boolean search(File file) {
try {
try (Scanner in = new Scanner(file, "UTF-8")) {
boolean found = false;
while (!found && in.hasNextLine()) {
String line = in.nextLine();
if (line.contains(keyword)) found = true;
}
return found;
} catch (IOException e) {
return false;
}
}
}
}
Ref: