1、只有线程池中的任务需要无限期的等待一些必须由池中其他任务才能提供的资源或条件,就有可能会发生线程饥饿死锁。
2、可以通过限定任务等待资源的时间来缓解执行时间较长的任务造成的影响。如果等待超时,可以把任务标识为失败,然后中止任务或者将任务重新放回队列以便随后执行。
3、线程池的理想大小取决于被提交任务的类型以及所部署系统的特性。需要避免过大和过小这两种极端情况。对于计算密集型的任务,在拥有N个处理器的系统上,当线程池的大小为N+1时,通常能实现最优的利用率。
4、线程池的基本大小是线程池的目标大小,即在没有任务执行时线程池的大小,线程池的最大大小表示可同时活动的线程数量的上限。如果某个线程的空闲时间超过了存活时间,那么将被标记为可回收的。
5、ThreadPoolExecutor允许提供一个BlockingQueue来保存等待执行的任务。基本的任务排队方法有3种:无界队列,有界队列和同步移交。对于非常大或者无界的线程池,可以通过使用SyncchronousQueue来避免任务排队,以及直接将任务从生产者移交给工作者线程,它不是一个真正的队列,而是一种在线程之间进行移交的机制。在newCachedThreadPool工厂方法中就使用了SynchronousQueue。
6、对于Executor,newCachedThreadPool工厂方法是一种很好的默认选择。
7、可以通过调用setRejectedExecutionHandler来修改ThreadPoolExecutor的饱和策略。JDK提供的饱和策略:AbortPolicy,CallerRunsPolicy,DiscardPolicy和DiscardOldestPolicy。
中止策略是默认的饱和策略,改策略将抛出未检查的RejectedExecutionException。
调用者运行策略实现了一种调节机制,改策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,也就是会在调用者线程执行。
8、ThreadPoolExecutor是可扩展的,它提供几个可在子类中改写的方法:beforeExecute,afterExecute和terminated,这些方法将在执行任务的线程中调用。无论任务是从run中正常返回,还是抛出一个异常而返回,afterExecute都会被调用。在线程池完成关闭操作时调用terminated,terminated可以用来释放Executor在其生命周期里分配的各种资源。
9、如果所有线程已固定的顺序来获得锁,那么在程序中不会出现锁顺序死锁问题。通过定义锁的顺序来解决动态的锁顺序死锁问题。在制定锁的顺序时,可以使用System.identityHashCode。
private static final Object tieLock = new Object();
public void transferMoney(final Object from, final Object to) {
int fromHash = System.identityHashCode(from);
int toHash = System.identityHashCode(to);
if (fromHash < toHash) {
synchronized (from) {
synchronized (to) {
// do something
}
}
} else if (fromHash > toHash) {
synchronized (to) {
synchronized (from) {
// do something
}
}
} else {
synchronized (tieLock) {
synchronized (from) {
synchronized (to) {
// do something
}
}
}
}
}
在极少数情况下,两个对象有相同的散列值,此时可以使用加时赛锁避免此问题。
10、如果在持有锁时调用某个外部方法,需要警惕死锁,因为这个外边方法可能会获取其他锁,或者阻塞时间过长。
11、在程序中尽量使用开发调用,收缩同步代码块的保护范围,使其仅被用于保护那些涉及共享状态的操作。
12、线程死锁的避免与诊断,在设计时考虑锁的顺序,尽量减少加锁交互数量,尽可能的使用开放调用。可以使用显示锁代替内置锁,显示锁可以指定一个超市时限,还可以通过dump 线程信息分析死锁。
13、如果可运行的线程数大于CPU的数量,那么操作系统最终会将某个正在运行的线程调度出来,从而使其他线程能够使用CPU。这将导致一次上下文切换,在这个过程中将保存当前运行线程的执行上下文,并将新调度进来的线程的执行上下文设置为当前上下文。
14、在synchronized和volatile提供的可见性保证中可能会使用一些特殊指令,即内存栅栏。内存栅栏可以刷新缓存,使缓存无效,刷新硬件的写缓冲,以及停止执行管道。
15、减少锁的竞争能够提高性能和可伸缩性。有3种方式可以降低锁的竞争程度:减少锁的持有时间,降低锁的请求频率,使用带有协调机制的独占锁,这些机制允许更高的并发性。缩短锁的持有时间可以将一些与锁无关的代码移出同步代码块。
16、如果一个锁需要保护多个相互独立的状态变量,那么可以将这个锁分解为多个锁,并且每个锁只保护一个变量,从而提高可伸缩性,并最终降低每个锁被请求的频率。
17、
网友评论