Java并发编程学习笔记(3)_JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > Java并发编程学习笔记(3)

Java并发编程学习笔记(3)

 2015/1/24 21:13:40  aoyouzi  程序员俱乐部  我要评论(0)
  • 摘要:8应用线程池有些类型的任务需要明确指定一个执行策略,如依赖性任务、采用线程限制的任务,对响应时间敏感的任务、使用ThreadLocal的任务。(只有当线程本地thread_local()值的生命周期被限制在当前任务种时,在池的某线程中使用ThreadLocal才有意义;在线程池中,不应该使用ThreadLocal传递任务间的数值)当任务都是同类的、独立时,线程池才有最佳的工作表现。8.1线程饥饿死锁在线程池中,如果一个任务依赖于其他任务的执行,就可能产生死锁。如
  • 标签:笔记 学习 Java 编程 学习笔记 Java并发

class="MsoNormal" style="line-height: 150%;">8应用线程

有些类型的任务需要明确指定一个执行策略,如依赖性任务、采用线程限制的任务,对响应时间敏感的任务、使用ThreadLocal的任务。(只有当线程本地thread_local()值的生命周期被限制在当前任务种时,在池的某线程中使用ThreadLocal才有意义在线程池中,不应该使用ThreadLocal传递任务间的数值)

?

当任务都是同类的、独立时,线程池才有最佳的工作表现。

?

8.1线程饥饿死锁

在线程池中,如果一个任务依赖于其他任务的执行,就可能产生死锁。如:对于一个单线程化的Executor,一个任务将另一个任务提交到相同的Executor中,并等待提交的任务的结果,总会引起死锁。

第二个任务滞留在工作队列中,直到第一个任务完成;但第一个任务不会完成,因为它在等待第二个任务的完成。——这就是线程饥饿死锁。

?

因此,无论何时,提交一个非独立的Executor任务时,要明确出现线程饥饿死锁的可能性,且在代码或者配置文件以及其他可以配置Executor的地方,任何有关池的大小和配置约束都要写入文档。

?

8.1.2耗时操作

耗时任务会造成线程地堵塞,还会拖长服务时间。解决之道:限定任务的等待资源的时间。

?

大部分平台类库中的阻塞方法,同时提供了限时、非限时的2版本,如Thread.join/BlockingQueue.put/CountDownLatch.await/Selector.select

?

8.2定制线程池大小

池的长度应该由某种配置机制来提供,不要硬编码,当然无须特别准确,只要以防过大或过小。

?

8.3配置ThreadPoolExcutor

核心池大小(Core Pool SIze)、最大池大小(max Pool Size)、存活时间(Keep Live Time)共同管理线程的创建与销毁。

?

核心池大小(Core Pool SIze)是目标的大小,线程池的实现试图维护池的大小:即没有任务执行,池的大小也等于核心池的大小;且直到工作队列充满时,池都不会创建更多的线程;

?

最大池大小(max Pool Size)是可同时活动的线程数的上限。如果一个线程已经闲置的时间超过了存活时间(Keep Live Time),会成为被回收的候选者,如果当前池的大小超过了核心池的大小,线程池会终止它。

?

Public ThreadPoolExecutor(int CorePoolSize,int maxPoolSize,long keepLiveTime,TimeUnit unit,……)

?

通过调节核心池的大小与存活时间,可以促进线程池归还空闲线程占有的资源。

?

newFixedThreadPool:工厂为请求的池设定了核心池的大小,最大池的大小,永不会超时

newCachedThreadPool:工厂将最大池大小设置为Integer.MAX_VALUE,核心池大小为0,超时1分钟

?

8.3.2管理队列任务

ThreadPoolExecutor允许提供了一个BlockingQueue来持有等待执行的任务,任务排队有3种基本方法:无限队列,有限队列,同步移交。

?

newFixedThreadPoolnewSingleThreadExecutor默认使用的是一个无限的LinkedBlockingQueue,如果所有的工作者线程都处于忙碌状态,任务将会在队列中等候;如果任务持续的快速到达,超过了它们被处理的速度,队列也会无限的增加。

?

稳妥的资源管理策略是使用有线的队列(如ArrayBlockingQueue、有限的LinkedBlockingQueuePriorityBlockingQueue

有界队列有助于避免资源耗尽的情况发生,但队列满时,要有相应的“饱和策略”处理。

?

newCachedThreadPool工厂提供了比定长的线程池更好的队列等候性能,它是Executor的一个很好的默认选择。

?

8.3.3饱和策略

当一个有线队列充满时,饱和策略开始生效。

ThreadPoolExecutor的饱和策略可以通过调用setRejectedExectionHandler来修改(如果任务提交到一个已经关闭的Executor时,也会用到饱和策略)

?

JDK提供了几种不同的RejectedExectionHandler实现,每一个实现不同的饱和策略:AbortPolicyCallerRunsPolicyDiscardPolicyDiscardOldestPolicy.

?

默认的中止(abort)策略引起Executor抛出异常;调用者捕获异常,编写满足自己需求的处理代码;

当最新提交的任务不能进入队列等候执行时,“遗弃(Discard)”策略会放弃这个任务;

“遗弃最旧的(Discard——oldest)”策略选择丢弃的任务,是本应接下来执行的任务,该策略还会尝试重新提交新任务。(如果工作队列是优先级队列,那么遗弃最旧的策略选择丢弃的是刚好优先级最大的元素,所以混合使用遗弃最旧的饱和策略与优先级不可行)

?

“调用者运行(Call_runs)”策略的实现形式,既不会丢弃哪个任务,也不会抛出异常。它会把一些任务推回到调用者那里,以此缓解新任务流。它不会在线程池中执行最新的任务,但会在一个调用了executor的线程中执行。

?

8.3.4线程工厂

线程池创建线程,要通过一个线程工厂实现。默认的线程工厂创建了一个新的、非后台的线程。

ThreadFactory只有唯一的方法: .newThread()。它会在线程池需要创建一个新线程时调用。

?

8.4拓展ThreadPoolExecutor

它提供了几个“钩子”让子类去覆盖——beforeExecuteafterExecuteterminate——用于拓展ThreadPoolExecutor的行为。

(可以用于添加日志、时序、监视器、统计信息)

?

无论是正常从run返回,还是抛出异常,afterExecute会调用。

Terminate线程池关闭时调用。

?

Terminate可以用于释放Executor在生命周期中分配到的资源,还可以发出通知、记录日志、完成统计信息。

?

8.5并行递归算法

当每一个迭代彼此独立,且完成循环时,每个迭代的工作意义都足够大,足以弥补管理一个新任务的开销时,这个顺序循环是适合并行的。

?

9 GUI应用程序

几乎所有的GUI工具集都实现为“单线程化子系统”,意味着所有GUI的活动都被限制在一个单独的线程中,如SwingSWT

?

早期GUIGUI事件在“主事件循环”进行处理;

现代GUI:创建一个专门的线程,事件派发线程EDI来处理GUI事件

(多线程GUI易受死锁影响)

?

单线程化的GUI框架通过线程限制来达到线程安全性,所有GUI中的对象,包括可视化组件和数据模型,都只能被事件线程访问。

?

Swing的线程限制

Swing的单线程规划:Swing的组件和模型只能在事件分派线程中被创建、修改和请求。

?

?

发表评论
用户名: 匿名