Java并发编程实战(二)

第六章 任务执行

在线程中执行任务(找出清晰地任务边界以及明确的任务执行策略)
1.串行地执行任务:主线程在接受连接和处理间交替运行
2.显式地为任务创建线程:为每个请求创建新的线程A.任务处理过程从主线程分离B.可以并行处理C.处理代码必须线程安全,会有多个任务并发调用处理代码
3.创建线程的缺点:A.线程生命周期开销大 B.内存和CPU消耗大 C.线程最大数量存在限制

Executor框架(强大的异步功能将任务的提交和执行解耦,基于生产者-消费者模式)

  1. Executor仅有一个方法:e.execute(runnable r),无返回值(若需返回值,用submit)
    2.基于Executor的web服务器(长度固定的线程池)可通过改写方法达到不同效果:A.thread®.start():所有线程异步执行,B.r.run():线程同步执行
    3.执行策略特点:A.取决于计算资源 B.限制并发任务数量 C.将任务的提交和执行分离 D.每当看到new Thread(runnable).start(),并希望更灵活的执行策略,用Executor代替Thread
    4.线程池(管理工作线程的资源池)特点:A.与工作队列(保存等待执行的任务)密切相关 B.重用现有线程而非创建新线程(节省开销) C.可调整线程池大小保持忙碌并防止耗尽内存
    5.线程池分类(将为任务分配线程变成基于线程池的策略):A.newFixedThreadPool(固定长度线程池):创建线程直到达到线程池最大量,若有结束创建新线程 B.newCachedThreadPool(可缓存线程池):不存在任何限制,超过需求则回收,不够则添加 C.newSingleThreadExecutor(单线程Executor):按照任务在队列中顺序来串行执行 D.newScheduledThreadPool(固定长度线程池):延迟或定时方法执行任务 E.TaskExecution WebServer中的Web服务器(使用有界线程池Executor):通过Executor提交到工作队列,工作线程反复从工作队列取出任务并执行它们

ExecutorServie的用法(继承自Executor,增加service特性)
1.Executor(若无法正确关闭,JVM将无法结束)扩展成ExecutorService接口用于生命管理周期的方法(初始时处于运行态):A.shutdown方法:执行完再关闭 B.shutdownNow方法: 通过“线程中断”(thread.interrupt),如果线程无法响应“中断”,那么将不会通过此方式被立即结束。shutdowNow返回等待执行的任务列表(List)(缺点:无法通过常规方法看哪些任务已开始却尚未结束)
2shutdownNow查看尚未结束即关闭的任务:封装ExecutorServie并用线程安全的集合记录哪些任务,收入集合的判断条件:if(isShutDown==true&&Thread.currentThread().isInTerrupted())
3. isShutdown:程序是否已经关闭,1)方法将导致其返回true。
4.isTerminated:是否已结束,若关闭后,所有任务都执行完成,返回true,否则返回false。
5.awaitTermination(timeout):会抛出interruptException,等待一段时间直到任务全部结束,若超时就返回false。
6.Future future=pool.submit(callable/runnale): A.submit提交实现Callable接口的任务,返回封装异步计算结果的Future B.submit提交实现Runnable接口任务,并指定调用Future.get()时返回的result对象C.submit提交实现Runnable接口的任务,返回封装异步计算结果Future
7.List invokeAll(Collection):同步方法,执行所有任务列表,当所有任务都执行完成后,返回Future列表,可以对一批任务进行批量跟踪。此方法会抛出interruptException
8.T invokeAny(Collection): 任务集合中,任何一个任务完成就返回。
9.ExecutorService关闭后:A.提交任务将由拒绝执行处理器处理:拒绝处理或使execute方法抛出未检查的RejectedExecutionException。B.任务完成,ExecutorService转入终止状态:可调用awaitTermination等待ExecutorService到达终止或isTerminated轮询ExecutorService是否终止

Runnable,Callable,Future,FutureTask区别(线程池中线程)
1.Runnable:只声明了run()方法A.不能返回一个值(void) B.未开始的任务可以取消,已开始的任务当他们相应中断时才能取消
2.Callable声明:public interface Callable,此方法可返回传递进来的V类型,也可抛出异常
3.Callable用法:配合ExecutorService使用,详情见ExecutorService.submit()重载
4.public interface Future接口作用:对具体的Runnable或者Callable任务A任务是否完成 B.能够中断任务 C.能够获取任务执行结果,代表任务生命周期
5.Future.get():A.任务完成,get立即返回或抛出Exception B.未完成,则阻塞直到任务完成 C.若抛出异常,则get将异常封装为ExecutionException并重新抛出D.若任务取消(用cancel),get抛出CancellationException,并可通过getCause获得被封装的异常
6. Future.cancel()用来取消任务,若取消成功则返回true,若取消失败则返回false
7.Future.isCancelled()表示任务是否被取消成功,若成功,返回true
8.Future.isDone()表示任务是否已经完成,若完成,则返回true
9.Future.get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null
10.FutureTask的声明:public class FutureTask implements RunnableFuture而public interface RunnableFuture extends Runnable, Future。所以它A.可以作为Runnable被线程执行 B.作为Future得到Callable的返回值
11. FutureTask.run()执行状态:A.没有被执行之前,FutureTask处于未启动状态B.被执行的过程中,FutureTask处于已启动状态C.执行完正常结束,或者被取消或者抛出异常而结束,FutureTask都处于完成状态。
12一般情况: 加入线程池运行,Runnable使用ExecutorService的execute方法,Callable使用submit方法。

找出可利用的并行性(Executor框架指定执行策略,Runnable或callable描述任务)
1.如何创建Future:A.ExecutorService中所有submit方法都返回一个Future B.显式为某个指定的Runnable或Callable实例化FutureTask C.ExecutorService改写AbstractExecutorService中的newTaskFor方法,从已提交的callable或runnable控制future实例化过程
2.CompletionService(融合Executor和BlockingQueue):执行Callable任务,使用类似队列操作take和poll方法获取结果,这些结果完成时封装为Future
3.ExecutorCompletionService的实现: A.构造函数中创建BlockingQueue保存计算结果 B.计算完成调用Future-task中done方法 C.提交任务时先包装为QueueingFuture(FutureTask子类),再改写done方法,将结果放入BlockingQueue中。
4. ExecutorCompletionService特点:可共享一个Executor,也可创建特定计算私有的
5.Future.get(作用:为任务设定时限):结果可用时立即返回,若没在时限内计算出结果,将抛出TimeoutException(超时后中止任务或通过Future取消任务)
6.invokeAll方法:A.参数为一组任务并返回一组Future添加到返回集合 B.invokeAll返回后,任务要么正常完成,要么取消。可用get或isCancelled判断

第七章 取消与关闭

任务取消(Java没有任何机制来安全终止线程,但提供了中断)
1.任务可取消的定义:某个操作正常完成前将其置入”完成”状态
2.设置”已请求取消”标志:A.若设置,将提前结束 B.定期检查标志 C.标志为volatile类型
3.设置标志的缺点:若生产者在put方法阻塞,将无法检测标志

中断(实现取消的最合理方式)
1.Thread中断方法:A.有boolen类型中断状态,中断时设为true B.interrupt方法中断目标线程 C.isInterrupted方法返回目标线程中断状态 D.interrupted清除当前线程中断状态
2.阻塞库方法(Thread.sleep或Object.wait):A.会检查何时中断,发现中断提前返回 B.响应中断的操作:清除中断,抛出InterruptedException,表示阻塞由于中断提前结束
3.非阻塞下中断:中断状态将被设置,不触发InterruptedException,将一直保持中断
4.中断操作含义:A.并非真正中断,而是发出中断请求,wait,sleep,join等将会处理请求 B.若执行时发现已设置中断,将抛出异常
5.调用静态interrupted:A.返回true,则屏蔽已有中断 B.若想防止,抛出InterruptedException异常,或者再次调用interrupt恢复中断
6.自定义机制如何和可阻塞库函数良好交互:使用中断而非boolean标志请求取消

中断策略(如何解释中断请求)
1.中断策略作用:寻找哪些工作单元是原子操作,多快速度响应,应做哪些工作
2.合理的中断策略:线程级取消或服务级取消,尽快退出,必要时清理,通知所有者线程
3.任务所在的执行线程:A.任务不在自己拥有的线程中执行,而在某个服务(线程池)拥有的线程中执行 B.对于非线程所有者代码,应小心保存中断状态,线程拥有着才能对中断做出响应 C.区分任务线程和中断反应,中断请求=取消线程池中某个工作者线程=取消当前任务
4.检查到中断时:任务不需要放弃所有操作,可推迟处理中断,但需记住中断,完成当前任务后抛出InterruptedException或表示已收到中断请求
5.注意事项:A.任务不该对执行该任务的线程的中断做出任务假设 B.每个线程拥有各自中断策略,除非知道中断对该线程的含义,否则不应该中断线程

响应中断
1.调用可中断的阻塞函数,如何处理InterruptedException传递异常,使你的方法也成为可中断的阻塞方法,传递和添加到throws子句一样
2.不想或无法传递InterruptedException:再次调用interrupt恢复中断
3.注意:只有实现线程中断策略才可以屏蔽中断请求,常规任务和库代码都不应该屏蔽
3.不支持取消但可调用可中断阻塞方法操作:循环中调用方法,发现中断后在本地保存中断调用interrupted暂时阻塞中断,返回前再恢复状态
4.代码不会调用可中断的阻塞方法:可通过任务代码中轮询当前线程的中断状态来响应中断
5.取消过程:可能涉及其他状态,可获得由中断保存的信息,并未中断的线程提供进一步指示
6.在外部线程安排中断缺点:不了解中断策略,取消任务结果不一定好
7.Thread.join方法(可限时):把指定线程加入当前线程,将两个执行线程合并为顺序执行

ScheduledExecutorService和Timer类(继承ExecutorService)
1.作用: 为了支持时间可控的任务执行而设计,其中包括:固定延迟执行,周期性执行
2.schedule(Callable callable, long delay, TimeUnit unit)或schedule(Runnable command, long delay, TimeUnit unit)方法:固定延时
3.scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)方法:循环任务,按照上一次任务的发起时间计算下一次任务的开始时间
4.scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)方法:循环任务,以上一次任务的结束时间计算下一次任务的开始时间
5.Timer类缺点:A.定时任务只创造一个线程,若执行时间过长将破坏其他TimerTask定时精确性 B.Timer线程不捕获异常,若抛出未检查异常,不会恢复线程执行,错误认为整个Timer都被取消,称为线程泄露。可使用DelayQueue代替 C. Timer的内部只有一个线程,多任务执行时,延迟时间和循环时间会出现问题,可考虑使用ScheduledExecutorService

处理不可中断的阻塞(必须知道阻塞的原因)
1.普通阻塞方法响应中断:通过提前返回或抛出InterruptedException异常来响应
2.Java.io包中的同步Socket I/O:通过关闭底层的套接字,可以使由于执行read或write等方法而被阻塞的线程抛出一个socketException
3.Java.io包中的同步I/O:A.当中断一个正在InterruptibleChannel上等待的线程,将抛出ClosedByInterruptException并关闭链路(其他在此链路阻塞的线程同样抛出异常) B.关闭一个InterruptibleChannel,将导致在链路操作上阻塞的线程都抛出AsynchronousCloseException
4.Selector的异步I/O:如果线程在调用Selector.select方法时阻塞,调用close或wakeup方法会使线程抛出ClosedSelectorException并提前返回
5.获取某个锁:线程等待内置锁而阻塞,将无法响应中断(认为一定能获得锁)。Lock类提供了lockInterruptibly方法,该方法允许等待一个锁同时响应中断

采用newTaskFor来封装非标准的取消(ThreadPoolExecutor新增功能)
1.newTaskFor特性:A.是一个工厂方法,将创建Future来代表任务 B.newTaskFor还能返回一个RunnableFuture接口,该接口扩展了Future和Runnable(由FutureTask实现)
2.通过定制表示任务的Future改变Future.cancel行为: A.实现日志记录或收集取消操作的统计信息,取消不响应中断的操作 B.改写此方法,ReaderThread可取消基于套接字的线程

停止基于线程的服务(服务由应用程序创建)
1.线程所有权问题:A.线程池是其工作线程所有者,其他不能对该线程操作(中断或修改优先级等) B.所有权不可传递,因此应用程序不能直接停止工作者线程 C.线程API中没对线程所有者下定义:线程由Thread对象biaoshi,并像其他对象一样可以被自由共享
2.持有线程的服务(例如线程池):服务的存在时间若大于创建线程的方法的存在时间,则应该提供生命周期方法来关闭它自身和它拥有的线程
3.示例如何关闭日志服务:生产者基于BlockingQueue阻塞队列,消费者:打印生产者队列内容
4.方法一:queue.take方法可响应中断,当捕捉到InterruptedException时退出,利用中断停止消费者。缺点:A.丢失正在等待打印日志信息 B.生产者未停止,其队列会阻塞,其他线程调用时会阻塞(不调用take无法解除阻塞)
5.方法二:设置”已请求关闭”标志,避免继续提交日志。缺点:存在竞态条件(先判断再运行)
6.方法三:通过原子方式检查关闭请求,并且添加原子计数器来统计消息个数(取出后减一)
7.将ExecutorService封装在更高级别的服务:可将所有权链从应用程序扩展到服务以及线程,所有权链上各个成员将管理它所拥有的服务或线程的生命周期
8.毒丸(另一种关闭生产者-消费者模式):一个放在队列上的对象,当得到它时,消费者立即停止(确保消费者完成毒丸之前提交的所有工作),生产者提交毒丸后也不再提交任务工作
9.毒丸适用范围:A.生产者和消费者都已知且无界队列 B.生产者消费者数量大时难以使用
10.只执行一次的服务:使用私有Executor,且该Executor生命周期受限于方法调用

处理非正常的线程终止(线程故障,程序可能仍可以运行)
1.线程提前死亡主要原因:RuntimeException,表示出现编程错误或不可修复错误,不会被捕捉。不会在调用栈逐层传递,默认在控制台中输出栈追踪信息
2.线程池工作者线程框架(ThreadPoolExecutor和Swing):若抛出异常线程终结,首先通知框架,若无足够线程满足,则创建新线程
3.线程由于未捕获异常退出后:JVM将事件报告给引用程序提供的UncaughtExceptionHandler异常处理器。若无任何异常处理器,默认行为将栈追踪信息到System.err
4.异常处理器处理未捕获异常:A.将错误信息以及相应的栈追踪信息写入应用程序日志中 B.尝试重新启动线程,关闭应用程序 C.执行其他修复或诊断等操作
5.为线程池中所有线程设置UncaughtExceptionHandler:需要为ThreadPoolExecutor的构造函数提供一个ThreadFactory(只有线程所有者才能改变)
6.注意事项:A.标准线程池允许发生未捕获异常时结束线程,但由于使用try-finally代码块,将有新线程代替 B.若没提供捕获异常处理器或其他故障通知机制,任务会失败 C.若希望任务发生异常而失败获得通知,并执行任务恢复操作,可将任务封装在能捕获异常的Runnable或Callable中,或改写ThreadPoolExecutor的afterExecute方法

JVM关闭
1关闭钩子定义:通过Runtime.addShutdownHook注册的尚未开始的线程
2.正常关闭顺序:A.JVM首先调用所有已注册的关闭钩子(无调用顺序),若有线程仍在运行,则将于关闭进程并发执行 B.当所有关闭钩子都执行结束,runFinalizersOnExit为true,则JVM将运行终结器,再停止 C.JVM不停止或中断任何关闭时仍运行的线程,JVM结束时,这些线程将被强行结束 D.若关闭钩子或执行器未执行完成,则正常关闭进程”挂起”且JVM必须被挂起。强行关闭只会关闭JVM,不关闭钩子
3.关闭钩子应线程安全:A.访问共享数据时必须使用同步机制,且避免发生死锁 B.不应对应用程序状态或JVM原因做假设 C.应尽快退出,否则会延长JVM结束时间
4.守护线程定义:执行辅助工作,又不阻碍JVM的关闭
5.线程分类(守护和普通):A.除主线程外,其他都是守护线程 B.创建新线程将继承创建它线程的守护状态 C.主线程创建的所有线程都是普通线程
6.线程差异:A线程退出时,JVM会检查其他运行的线程,若都是守护线程则正常退出 B.JVM停止时,所有存在的守护线程都被抛弃,直接退出(所以尽量少使用守护线程)
7.注意点:守护线程通常不能用来替代应用程序管理程序中各个服务的生命周期
8.终结器(finalize方法)作用:句柄或套接字句柄等不需要时,需显式交还给操作系统,垃圾回收器会调用这些对象finalize方法,保证持久化资源被释放
9.终结器:其访问的任何状态都可被多个线程访问,所以访问操作须同步(尽量不用终结器)

第八章 线程池的使用

在任务与执行策略之间的隐形耦合(有些类型任务需明确指定执行策略)
1.依赖性任务:若提交给线程池的任务需要依赖其他任务,则隐含给执行策略带来约束
2.使用线程封闭机制的任务:任务要求执行所在Executor为单线程,若将Executor从单线程环境改为线程池环境,将会失去线程安全性
3.对响应时间敏感任务:例如运行长时间任务,则降低由该Executor管理的服务相应性
4.使用ThreadLocal任务:只有当线程本地值得生命周期受限于任务生命周期时,线程池中使用Threadlocal才有意义,否则不应使用Threadlocal在任务间传递值
5.线程池性能最佳:任务都是同类型且相互独立的。遇以上情况应尽量使线程池大
6.线程饥饿死锁的产生:线程池中任务需要无限期等待一些必须由池中其他任务才能提供的资源或条件,除非线程池足够大,否则会产生线程饥饿死锁
7.线程饥饿死锁处理:在代码或配置Executor的配置文件中记录线程池大小限制或配置限制
8.运行时间较长任务:限定任务等待资源的时间(阻塞方法限时版)或增加线程池规模

设置线程池大小(取决于被提交任务类型以及所部署系统的特性)
1.计算密集型任务:拥有Ncpu个处理器的系统上,线程池大小为Ncpu+1,可有最优利用率
2.包含I/O操作或阻塞操作的任务:线程不会一直执行,线程池应尽可能大
3.设置线程池大小公式:A.W/C(任务等待时间和计算时间比值) B.Ncpu(CPU个数),Ucpu(CPU利用率0<U<1) C.线程池最优大小公式Nthread=NcpuUcpu(1+W/C) D动态获取Ncpu:int Ncpu=Runtime.getRuntime().availableProcessors
4.注意事项:CPU并非唯一影响线程池大小资源,其他资源可用总量除以每个任务需求量也可

配置ThreadPoolExecutor(为Executor提供基本实现,并在此基础上修改)

线程的创建与销毁
1.线程池基本大小:没有任务执行时线程池大小,线程池空闲大小超过基本大小,将终止
2.线程池最大大小:同时活动线程数量的上限,工作队列满了才会创建超过基本大小
3.存活时间:某个线程空闲时间超过存活时间,将被标记为可回收
4.各种工厂方法:A.newFixedThreadPool工厂方法:将线程池基本大小和最大大小设置成参数中指定的值,且创建线程池不会超时 B.newCachedThreadPool工厂方法:将线程池最大大小设置为Integer Max_value,基本大小为0,超时为1分钟(线程池可无限扩张,无需求时收缩)

管理队列任务
1.无限制创造线程:新到的请求在Executor管理的Runnable队列等待,通过一个Runnable和一个链表结点来表示等待中的任务(缺点:开销低但仍会耗尽资源,或者出现请求突增)
2.利用ThreadPoolExecutor的BlockingQueue来保存等待执行的任务:A.基本任务安排:无界队列,有界队列,同步移交 B.队列的选择:与配置参数有关(例如线程池大小)
3.无界队列LinkedBlockingQueue特点(newFixedThreadPool和newSingleThreadExecutor默认使用):A所有工作者线程都忙碌,任务在队列等候 B.任务到达速度超过线程池处理速度,队列将无线增加,响应性能会越来越糟
4.有界队列(更稳妥,如ArrayBlockingQueue,有界LinkedBlockingQueue,PriorityBlockingQueue等):A.优点:有助于避免资源耗尽 B.有界队列大小须和线程池大小一起调节
5.同步移交(无限大的线程池不排队):通过使用SynchronousQueue(并非真正队列,一种移交机制),直接将任务从生产者移交给工作者线程。
6. SynchronousQueue特点:A.欲将元素放入其中,必须有另一个线程等待接收。若无线程等待接收但线程池大小小于最大值,则ThreadPoolExecutor将创造新线程。否则任务被拒绝 B.优点:更高效,且只有线程池无界或可以拒绝任务时SynchronousQueue才有效
7.控制任务执行顺序:A.使用LinkedBlockingQueue或ArrayBlockingQueue这样的FIFO(先进先出)队列 B.PriorityBlockingQueue(根据优先级安排任务),任务的优先级通过自然顺序或Comparator(若任务实现Comparable)定义
8.newCacheThreadPool工厂方法(使用了SynchronousQueue)适用场景:A任务间存在依赖性,有界线程池或队列可能导致线程饥饿死锁,应使用无界线程池 B.可提供比固定大小线程池更好的排队性能(当需要限制当前任务数量时使用固定大小线程池,否则易过载)

饱和策略(有界队列填满后发挥作用)
1.ThreadPoolExecutor饱和策略:A通过调用setRejectedExecutionHandler修改 B.若任务被提交到已关闭的Executor也会用到饱和策略 C.不同的RejectedExecutionHandler包含不同的饱和策略:AbortPolicy,CallerRunsPolicy,DiscardRunsPolicy,DiscardPolicy和DiscardOldestPolicy
2.Abort中止(默认的饱和策略):抛出未检查的RejectedExecutionException,调用者捕获此异常,根据自己的需要编写自己的处理代码
3.Discard抛弃:新提交任务无法保存到队列中等待执行,此策略会悄悄抛弃任务
4. DiscardOldest抛弃最旧的:抛弃下一个被执行任务(若是优先队列,抛弃优先级最高的)
5.Caller-Runs调用者运行策略:A.不抛弃任务,不抛出异常,而将某些任务回退到调用者 B.不会在线程池某个线程执行新提交的任务,而在一个调用execute的线程中执行 C.缺点:调用execute的线程一段时间不能提交任务,可能线程池-工作队列-应用程序-TCP层逐步过载D.构造:创建Executor时可对饱和策略或执行策略修改(executor.setRejectedExecutionHandler)
6.调用者运行的饱和策略:通过使用Semaphore(信号量)来限制任务到达率(正在执行和等待执行的任务数量).。例如无界队列+信号量。

线程工厂(线程池创建一个线程都通过线程工厂方法)
1.ThreadFactory:只定义newThread方法,创建新线程都调用此方法(可定制线程池配置信息)
2.定制线程工厂作用:A.线程池中的线程制定一个UncaughtExceptionHandler B.实例化一个定制的Thread类用于执行调试信息的记录 C.修改线程优先级或守护状态
3.定制Thread用途:A为线程指定名字 B.设置自定义UncaughtExceptionHandler向Logger写入信息 C.维护统计信息(线程被创建或销毁个数) D.线程被创建或终止时调试信息写入日志
4.程序用安全策略控制访问权限:通过Executors中privilegedThreadFactory工厂定制自己的线程工厂,其创建的线程与其有相同的访问权限、AccessControlContect和contextClassLoader,若使用execute或submit从客户程序继承访问权限,会导致安全性异常

如何在调用构造函数后再定制ThreadPoolExecutor
1.调用ThreadPoolExecutor构造函数后:设置函数setter修改大部分传递给它构造函数的参数
2.Executor通过Executors中工厂方法(newSingleThreadExecutor除外)创建:可将结果类型转换为ThreadPoolExecutor以访问设置器
3.Executors包含一个unconfigurableExecutorService工厂方法:A.其对一个现有ExecutorService包装进行,使其只暴露ExecutorService方法,所以不能配置 B. newSingleThreadExecutor返回按此方法封装的ExecutorService,而非最初ThreadPoolExecutor(用于防止执行策略被修改)

扩展ThreadPoolExecutor
1.ThreadPoolExecutor子类化中可改写的方法:beforeExecute,afterExecute,terminated
2. beforeExecute和afterExecute等方法:在执行任务的线程中调用,方法中可添加日志,计时,监视或统计信息收集 C. :若beforeExecute抛出一个RuntimeException,则任务不被执行,且afterExecute不被调用
3.afterExecute:A.任务从run中正常返回或抛出异常返回,都会被调用 B.若任务完成后带有Error,则不会被调用
4.terminated:A.何时调用:线程池所有任务都已完成且所有工作者线程也已关闭后调用 B.作用:释放Executor在其生命周期里分配的各种资源,还可执行发送通知,记录日志,收集finalize统计信息等操作
5.示例: :A.beforeExecute把值保存在ThreadLocal变量中,再由afterExecute读取 B.使用两个AtomicLong变量,分别记录已处理任务数和总处理时间,通过terminated输出日志消息

递归算法的并行化
1.并行化条件:循环体包含密集型计算,或执行可能阻塞的I/O操作,且每次迭代是独立的
2.并行循环:A.适用环境:循环中迭代操作都是独立的,无需等待所有迭代都完成再执行,且迭代操作执行量比管理新任务开销大B.方法:可使用Executor将串行循环转化为并行循环
3.利用Executor构造的processInParallel:所有下载任务都进入Executor队列后立刻返回,不会等待这些任务全部完成(若需提交一个任务并等待完成,用ExecutorService.invokeAll,执行完后调用CompletionService获取结果)
4.递归设计采用循环并行化的情况:每个迭代操作都不需要来自后续递归迭代的结果(例如深度优先遍历,便利过程串行,compute调用为并行执行)
5.递归并行化数据保存(BFS,DFS都可):使用ConcurrentHashMap(提供线程安全,且避免更新合集时存在静态条件),putIfAbsent(原子方式)添加到集合
6.递归并行化后找到结果后如何停止(或第一个找到解答后,需要对其他任务更新解答):闭锁机制,即包含结果的闭锁(可使用CountDownLatch,并使用锁定机制确保解答只被设置一次)
7.递归并行化中若遍历所有可能但不存在解答:A.缺点:在getSolution调用将永远等待下去 B.解决方法:记录活动任务数量,当该值为零时将解答设置为null
8.递归并行化结束条件(解答时间可能比等待时间长):A.时间限制,在ValueLatch实现一个限时getValue,若其超时,关闭Executor并声明出现失败 B.其本身约束条件:可能是只处理特定数量的任务 C.还可提供取消机制,由用户决定何止停止

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值