引入了一个”执行服务”的概念,它将”任务的提交”和”任务的执行”相分离,”执行服务”封装了任务执行的细节,对于任务提交者而言,它可以关注于任务本身,如提交任务、获取结果、取消任务,而不需要关注任务执行的细节,如线程创建、任务调度、线程关闭等。
基本接口
任务执行服务涉及的基本接口:
Runnable和Callable:表示要执行的异步任务
Executor和ExecutorService:表示执行服务
Future:表示异步任务的结果
Executor和ExecutorService
Executor表示最简单的执行服务,其定义为:
public interface Executor {
void execute(Runnable command);
}
就是可以执行一个Runnable,没有返回结果。接口没有限定任务如何执行,可能是创建一个新线程,可能是复用线程池中的某个线程,也可能是在调用者线程中执行。
ExecutorService扩展了Executor,定义了更多服务,基本方法有:
public interface ExecutorService extends Executor {
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
//… 其他方法
}
这三个submit都表示提交一个任务,返回值类型都是Future,返回后,只是表示任务已提交,不代表已执行,通过Future可以查询异步任务的状态、获取最终结果、取消任务等。我们知道,对于Callable,任务最终有个返回值,而对于Runnable是没有返回值的,第二个提交Runnable的方法可以同时提供一个结果,在异步任务结束时返回,而对于第三个方法,异步任务的最终返回值为null。
Future
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit) throws InterruptedException,
ExecutionException, TimeoutException;
}
get用于返回异步任务最终的结果,如果任务还未执行完成,会阻塞等待,另一个get方法可以限定阻塞等待的时间,如果超时任务还未结束,会抛出TimeoutException。
cancel用于取消异步任务,如果任务已完成、或已经取消、或由于某种原因不能取消,cancel返回false,否则返回true。如果任务还未开始,则不再运行。
isDone和isCancelled用于查询任务状态。
get方法,任务有三个结果:
1>正常完成,get方法会返回其执行结果,如果任务是Runnable且没有提供结果,返回null
2>任务执行抛出了异常,get方法会将异常包装为ExecutionException重新抛出,通过异常的getCause方法可以获取原异常
3>任务被取消了,get方法会抛出异常CancellationException
如果调用get方法的线程被中断了,get方法会抛出InterruptedException。
总结
Future是一个重要的概念,是实现”任务的提交”与”任务的执行”相分离的关键,是其中的”纽带”,任务提交者和任务执行服务通过它隔离各自的关注点,同时进行协作。
示例
public class BasicDemo {
static class Task implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sleepSeconds = new Random().nextInt(1000);
Thread.sleep(sleepSeconds);
return sleepSeconds;
}
}
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(new Task());
// 模拟执行其他任务
Thread.sleep(100);
try {
System.out.println(future.get());
} catch (ExecutionException e) {
e.printStackTrace();
}
executor.shutdown();
}
}
死锁示例
public class DeadLockDemo {
private static Object lockA = new Object();
private static Object lockB = new Object();
private static void startThreadA() {
Thread aThread = new Thread() {
@Override
public void run() {
synchronized (lockA) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
synchronized (lockB) {
}
}
}
};
aThread.start();
}
private static void startThreadB() {
Thread bThread = new Thread() {
@Override
public void run() {
synchronized (lockB) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
synchronized (lockA) {
}
}
}
};
bThread.start();
}
public static void main(String[] args) {
startThreadA();
startThreadB();
}
}