Future
在并发编程中,我们经常用到非阻塞的模型,在之前的多线程的三种实现中,不管是继承thread类还是实现runnable接口,都无法保证获取到之前的执行结果。通过实现Callback接口,并用Future可以来接收多线程的执行结果。
Future表示一个可能还没有完成的异步任务的结果,针对这个结果可以添加Callback以便在任务执行成功或失败后作出相应的操作。接口和类:
Future接口定义了主要的5个接口方法,有RunnableFuture和SchedualFuture继承这个接口,以及CompleteFuture和ForkJoinTask。
RunnableFuture(接口)
这个接口同时继承Future接口和Runnable接口,在成功执行run()方法后,可以通过Future访问执行结果。
FutureTask (实现类)
FutureTask 提供了Future的基本实现,可以实现了启动和取消一个方法,查询这个方法是否已完成。一旦计算完成,这个计算将不能被重启和取消,除非调用runAndReset方法。
FutureTask能用来包装一个Callable或Runnable对象,因为它实现了Runnable接口,而且它能被传递到Executor进行执行。为了提供单例类,这个类在创建自定义的工作类时提供了protected构造函数。 通过CAS操作保证状态的变更,通过自旋锁+线程挂起+唤醒的方式来实现阻塞实现类:FutureTaskFuture的主要方法
get()方法可以当任务结束后返回一个结果,如果调用时,工作还没有结束,则会阻塞线程,直到任务执行完毕
get(long timeout,TimeUnit unit)做多等待timeout的时间就会返回结果cancel(boolean mayInterruptIfRunning)方法可以用来停止一个任务,如果任务可以停止(通过mayInterruptIfRunning来进行判断),则可以返回true,如果任务已经完成或者已经停止,或者这个任务无法停止,则会返回false.isDone()方法判断当前方法是否完成isCancel()方法判断当前方法是否取消PS
FutureTask 可以直接当做Runnable实现类传给 Thread 执行或者 放在executorService 的submit 中,FutureTask 中的 run() 方法,会判断类型是不是call()方法。AbstractExecutorService 中有submit(call) 方法,
publicFuture submit(Callable task) { if (task == null) throw new NullPointerException(); RunnableFuture ftask = newTaskFor(task);}protected RunnableFuture newTaskFor(Callable callable) { return new FutureTask (callable);}
Java提供的线程池
FixedThreadPool
容量固定的线程池。活动状态和线程池容量是有上限的线程池。所有的线程池中,都有一个任务队列。只有核心线程,且数量固定,没有非核心线程。使用的是BlockingQueue<Runnable>作为任务的载体。当线程有空闲的,自动从队列中取出任务执行。
使用场景: 大多数情况下,使用的线程池,首选推荐FixedThreadPool。OS系统和硬件是有线程支持上限。不能随意的无限制提供线程池。
队列默认的容量上限是Integer.MAX_VALUE,基本就是无界队列。
常见的线程池容量: PC - 200。 服务器 - 1000~10000
CachedThreadPool
缓存的线程池。容量不限(Integer.MAX_VALUE)。自动扩容。容量管理策略:如果线程池中的线程数量不满足任务执行,创建新的线程。默认线程空闲60秒,自动销毁。
应用场景: 内部应用或测试应用。 如:电信平台夜间执行数据整理(有把握在短时间内处理完所有工作,且对硬件和软件有足够的信心)。 测试应用,在测试的时候,尝试得到硬件或软件的最高负载量,用于提供FixedThreadPool容量的指导。
SingleThreadExceutor
单一容量的线程池。
使用场景: 保证任务顺序时使用。如: 游戏大厅中的公共频道聊天。秒杀。
ForkJoinPool
分支合并线程池(mapduce类似的设计思想)。适合用于处理复杂任务。初始化线程容量与CPU核心数相关。
线程池中运行的内容必须是ForkJoinTask的子类型(RecursiveTask,RecursiveAction)。RecursiveTask有返回结果的分支合并任务,RecursiveAction无返回结果的分支合并任务。(Callable/Runnable)compute方法:就是任务的执行逻辑。
ForkJoinPool没有所谓的容量。默认都是1个线程。根据任务自动的分支新的子线程。当子线程任务结束后,自动合并。所谓自动是根据fork和join两个方法实现的。
应用: 主要是做科学计算或天文计算的。数据分析的。