文章目录
并发笔记二
Future和Callable
默认情况下,线程Thread对象不具有返回值的功能,若在需要取得返回值的情况下可以使用Java5并发包中的Future和Callable使线程具有返回值的功能
Future和Callale的介绍
Future是返回值处理, Callable接口和线程紧密相关,等同于有返回值的Runnable,调用call方法处理线程,且call方法可以声明抛出异常,而Runnable接口的run方法不可以声明抛出异常
执行完Callable接口中的任务后,返回值通过Future接口进行获得
Future-api:
boolean cancel(boolean mayInterruptIfRunning); // 若线程正在运行则是否中断正在运行的线程(mayInterruptIfRunning:true中断;false:不中断)
if(Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
// cancel返回true表示线程处于中断状态,此时此处真正中断该线程
}
cancel的返回值表示发送取消任务的命令是否成功完成
boolean isCancelled(); // 是否取消了任务,返回true表示cancel执行成功
boolean isDone(); // 无阻塞特性,判断future是否已经获取到值,超时或阻塞等待没有获取到返回false,执行过get且未超时返回true
V get()
V get(long timeout, TimeUnit unit) // 在指定的最大时间内等待获得返回值,若超时抛出TimeoutException异常
Callable执行线程
ExecutorService的submit方法:
ExecutorService的submit(Callable)方法可以执行Callable的任务,返回的Future接口的实现类通过get方法获得返回值
public class FutureTest {
private static ThreadFactory factory = new ThreadFactoryBuilder().setNameFormat("线程工厂").build();
private static ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 8, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>(), factory);
public static void main(String[] args) throws InterruptedException {
try {
Future<String> future = executor.submit(() -> {
Thread.sleep(3000);
return "test";
});
System.out.println("阻塞等待结果" + future.isDone());
System.out.println(future.get());
System.out.println("获得结果" + future.isDone());
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
结果
阻塞等待结果false
test
获得结果true
同时submit(Runnable)支持无返回值的runnable线程任务,此时Future的get获得的值为null
而方法submit(Runnable, T result)其第二个参数result可以作为执行结果的返回值,而不需要使用get()方法获得
private static ThreadFactory factory = new ThreadFactoryBuilder().setNameFormat("线程工厂").build();
private static ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 8, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>(), factory);
private static void run2() throws InterruptedException{
try {
Map<String, Object> result = new HashMap<>(5);
Future<Map<String, Object>> future = executor.submit(() -> {
result.put("test", "test");
}, result);
System.out.println("阻塞等待结果" + future.isDone());
System.out.println(future.get());
System.out.println("获得结果" + future.isDone());
} catch (ExecutionException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
run2();
}
Future的实现类是FutureTask
Future
-RunnableFuture
--FutureTask
---ScheduledFutureTask
-ScheduledFuture
--RunnableScheduledFuture
---ScheduledFutureTask
Callable使用接口RejectedExecutionHandler处理异常,当线程池关闭后依然有任务要执行时,可以实现处理,即线程池的拒绝接口
execute和submit
- execute没有返回值,submit可以有返回值
- execute默认情况下直接抛出异常,不能捕获(线程池的UncaughtExceptionHandler来处理未捕获异常),但可以由ThreadFactory来捕获,而submit方法默认情况下,可以catch ExecutionException捕获异常
通过ExecutionException捕获到异常
try {
Future<Map<String, Object>> future = executor.submit(() -> {
throw new IllegalStateException("异常发生");
});
System.out.println(future.get());
} catch (ExecutionException e) {
System.out.println("捕获到异常!!!");
e.printStackTrace();
}
无法捕获异常
try {
executor.execute(() -> {
throw new IllegalStateException("异常发生");
});
} catch (Exception e) {
System.out.println("异常!!!");
e.printStackTrace();
}
Future的问题
Callable接口可以通过Future取得返回值(默认实现类FutureTask),但Future接口的get方法是阻塞调用,排队顺序等待任务结果,影响运行效率,且主线程无法保证首先获得的是最先完成任务的返回值,这个问题催生CompletionService在java5诞生
CompletionService
以异步的方式,生产新任务,处理已完成任务的结果,将执行任务和处理任务分离开进行处理。使用submit执行任务,take取得已完成的任务,并按照完成任务的时间顺序处理结果
CompletionService-api
Future<V> submit(Callable<V> task);
Future<V> submit(Runnable task, V result);
Future<V> take() throws InterruptedException; 具有阻塞特性,顺序获取最快执行完成的任务
Future<V> poll(); 无阻塞特性,获取并移除下一个已完成任务的Future,若不存在该任务则返回null
Future<V> poll(long timeout, TimeUnit unit) throws InterruptedException;
等待指定时间,此时间内获取到值则立即向下执行,超时也向下执行
CompletionService接口唯一的实现类ExecutorCompletionService,该实现类需要依赖于Executor接口,即ThreadPoolExecutor实现类对象
// 使用LinkedBlockingQueue作为完成队列
public ExecutorCompletionService(Executor executor) {
if (executor == null)
throw new NullPointerException();
this.executor = executor;
this.aes = (executor instanceof AbstractExecutorService) ?
(AbstractExecutorService) executor : null;
this.completionQueue = new LinkedBlockingQueue<Future<V>>();
}
// 提供完成队列completionQueue
public ExecutorCompletionService(Executor executor,
BlockingQueue<Future<V>> completionQueue) {
if (executor == null || completionQueue == null)
throw new NullPointerException();
this.executor = executor;
this.aes = (executor instanceof AbstractExecutorService) ?
(AbstractExecutorService) executor : null;
this.completionQueue = completionQueue;
}
特性
异步操作,通过take获得Future对象,再通过get()获取到值
CompletionService完全解决了Future阻塞的特性,即哪个任务先执行完,先获取取future
若CompletionService接口中没有任务被执行完,则take().get()依然阻塞
public static void run () {
try {
MyCalable calable = new MyCalable("username1", 5000);
service.submit(calable);
service.submit(new MyCalable("username2", 4000));
service.submit(new MyCalable("username3", 3000));
service.submit(new MyCalable("username4", 2000));
service.submit(new MyCalable("username5", 1000));
for (int i = 0; i < 5; i++) {
System.out.println(String.format("等待结果:第%d个返回值", (i + 1)));
System.out.println(service.take().get());
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
基本方法
take
该方法取得最先完成任务的Future对象,即按执行任务的速度,顺序获得Future对象
poll
该方法用于获取并移除表示下一个已完成任务的Future,若不存在这个任务,则返回null,且poll方法无阻塞特性
注意poll获得的任务会被移除,此时take无法再获得这个任务
for (int i = 0; i < 10; i++) {
Thread.sleep(500);
System.out.println(service.poll());
}
for (int i = 0; i < 5; i++) {
System.out.println(String.format("等待结果:第%d个返回值", (i + 1)));
System.out.println(service.take().get());
}
poll(long timeout, TimeUnit unit)会等待一段时间
异常
- 线程任务出现异常,调用了take没有调用get:不出现异常
- 线程任务出现异常,调用了take也调用了get:抛出异常
出现异常后,下面的线程任务的结果不再执行
- 线程任务出现异常,调用了poll没有调用get:不出现异常
- 线程任务出现异常,调用了poll也调用了get:抛出异常
出现异常后,下面的线程任务的结果不再执行
ExecutorService
api
void shutdown()
List<Runnable> shutdownNow()
boolean isShutdown()
boolean isTerminated()
boolean awaitTermination(long timeout, TimeUnit unit)
<T> Future<T> submit(Callable<T> task)
<T> Future<T> submit(Runnable task, T result)
Future<?> submit(Runnable task)
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
具有阻塞特性,获得所有任务完成的结果Future的List集合
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
有超时时间
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
具有阻塞特性,获得最先完成的第一个任务的结果,并将其他线程interrupt
<T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)
有超时时间
方法
invokeAny与invokeAll
invokeAny方法具有阻塞特性,作用是取得第一个完成任务的结果值,当第一个任务执行完成后,会调用interrupt方法将其他任务中断,故而可在这些任务中结合if(Thread.currentThread().isInterrupt()==true)决定任务是否继续运行
即线程a运行完之后,线程池将其他线程如线程b置位interrupt状态
private static ExecutorService executor = Executors.newCachedThreadPool();
private static List list = new ArrayList();
static {
Callable a = () -> {
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException("抛出异常,线程被中断");
}
return "测试1";
};
Callable b = () -> {
System.out.println("b执行");
if (Thread.currentThread().isInterrupted()) {
System.out.println("异常抛出");
throw new InterruptedException("抛出异常,线程被中断");
}
return "测试2";
};
list.add(a);
list.add(b);
}
public static void run() {
try {
System.out.println(executor.invokeAny(list));
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
run();
}
invokeAny方法,其全部线程出现则最终异常时最后出现的异常,先出现,后出现的异常不影响其他线程
invokeAll方法等全部线程任务执行完毕后,取得全部完成任务的结果值
invokeAll方法出现异常,若某一个任务正确地返回值时,则其他Callable抛出去的异常在main方法中是不被处理的
若全部任务都有异常,则最后Callable抛出的异常在main方法中被处理
ScheduledExecutorService
java中计划任务有Timer工具类提供了以计时器或计划任务的功能来实现按指定时间或时间间隔执行任务,但Timer没有池化,而是以队列的方式管理线程,高并发运行效率低,故而采用ScheduledExecutorService对象来解决效率与定时任务的功能
api
public interface ScheduledExecutorService extends ExecutorService
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)
public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit)
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,imeUnit unit)
period周期时间,以任务开始时间开始
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit)
delay为多个任务之间的时间间隔,以任务结束时间开始
ScheduledExecutorService接口允许Runnable和Callable任务
ScheduledExecutorService主要用于将定时任务与线程池功能结合使用
其主要的实现类为ScheduledThreadPoolExecutor,ScheduledThreadPoolExecutor类同时继承ThreadPoolExecutor
public class ScheduledThreadPoolExecutor
extends ThreadPoolExecutor
implements ScheduledExecutorService
综合ScheduledExecutorService和ThreadPoolExecutor
其中submit和shutdown等重写
getQueue:取得队列中的任务,即还没运行将运行的任务,正在执行的任务不在此列
remove:移除任务参数为ScheduledFuture的数据类型 如(Runnable) future
ScheduledFuture future = executor.scheduleAtFixedRate()
executor.remove((Runnable) future)
setExecuteExistingDelayedTasksAfter:当对ScheduledThreadPoolExecutor执行了shutdown方法时,任务是否继续运行,默认值true(任务继续运行),而使用setExecuteExistingDelayedTasksAfter(false)时,任务被shutdown则不再运行
注意:该方法只能用于schedule和shutdown联合使用
setContinueExistingPeriodicTasksAfterShutdownPolicy:该方法传参true作用是当使用scheduleAtFixedRate方法或scheduleWithFixedDelay方法时,若调用ScheduledThreadPoolExecutor对象的shutdown方法,则任务还会继续运行,传参false则任务不运行,进程销毁
注意:该方法只能用于scheduleAtFixedRate和scheduleWithFixedDelay和shutdown联合使用
cacel(boolean):设定是否取消任务
setRemoveOnCancelPolicy:是否将取消后的任务从队列中清除
在队列中的任务可以取消,任务也不再运行
正在运行的任务可以停止,结合if(Thread.currnetThread().isInterrupted() == true)判断
示例
private static final ThreadFactory factory = new ThreadFactoryBuilder().setNameFormat("线程工厂").build();
private static final ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(10, factory);
private static final List<Callable<String>> list = new ArrayList<>();
static{
Callable a = () -> {
try {
String name = Thread.currentThread().getName();
System.out.println("a begin:" + name);
Thread.sleep(3000);
System.out.println("a return " + name);
} catch (Exception e) {
e.printStackTrace();
}
return "return_a";
};
Callable b = () -> {
try {
String name = Thread.currentThread().getName();
System.out.println("b begin:" + name);
System.out.println("b return " + name);
} catch (Exception e) {
e.printStackTrace();
}
return "return_b";
};
list.add(a);
list.add(b);
}
public static void run() {
try {
// 执行结束返回的是ScheduledFuture
ScheduledFuture<String> futureA = executor.schedule(list.get(0), 2L, TimeUnit.SECONDS);
ScheduledFuture<String> futureB = executor.schedule(list.get(1), 2L, TimeUnit.SECONDS);
System.out.println(String.format("a的返回值:%s", futureA.get()));
System.out.println(String.format("b的返回值:%s", futureB.get()));
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
run();
}
注意:
构造方法传的值为corePoolSize
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue(), threadFactory);
}
对于scheduleAtFixedRate方法,其作用为周期性执行任务,但是返回的ScheduledFuture对象无法获得返回值
,而schedule方法却可以获得返回值。故而使用scheduleAtFixedRate方法实现重复运行任务的效果时,需要结合自定义Runnable接口的实现类而不要使用FutureTask类,因为FutureTask类不能实现重复运行的效果
scheduleWithFixedDelay方法的作用在于设置多个任务之间固定的运行时间间隔,不同于scheduleAtFixedRate其间隔为固定间隔,scheduleWithFixedDelay的间隔为两个任务之间的间隔
603

被折叠的 条评论
为什么被折叠?



