序文:
如果你没有时间阅读这本,那你就来对地方了。小编会将每一章刨去废话,取其精华。分享给大家。如果觉得可以请不要忘了关注小编。我会定期跟新java 中的经典书籍。
正文:
第8章:线程池的使用
1.线程饥饿死锁:
在线程池中,如果任务依赖与其他任务,那么可能产生死锁。
例如:在单线程的Executor 中唯一的线程已经执行的任务代码中又提交一个任务,并且要依赖这个任务的结果,那么没有多余的线程取执行这个被提交的任务,永远得不到结果原来的任务也无法继续执行
同理,多个线程的线程池如果,正在执行的任务都由于等待其他处于等待队列中的任务而阻塞,那么会发生死锁。(等待的队列得不到线程取执行)
2.如何解决:
上面的原因都是线程中没有跟多的线程取中转队列中的任务:所以只要线程池中的线程足够大,就可以解决。(粗略)
3.每当提交一个又依赖性的Executor任务时,要清楚的地知道可能会产生线程“饥饿”死锁,因此需要在代码或配置Executor配置文件中记录线程池的大小限制或配置限制。(这样其他的程序员在用的时候就不要全部用完,留有中转的线程)
4.如果任务阻塞的时间过长,即使不出现死锁,线程池的响应性也很差。
执行时间长的任务如果不可避免,那么:a.这种长时间的任务数量要少于线程数量,(因为流出其他线程取执行短时间的任务,提高整体的性能)b.限定任务等待资源的时间(超时时间,大多数阻塞的方法都会实现这种方法)
5.线程池的理想大小:
代码中通常不固定线程的大小,通过某种配置机制来提供,或者根据Runtime.availableProcessors(cpu 的数目) 来动态计数。
线程池过大:大量的线程将在相对很少的CPU和内存资源上发生竞争(闲置线程也是占用资源的)很有可能耗尽资源
线程池过小:首先cpu 不能得到充分的利用,降低吞吐率
所以要设计理想大小的要考虑:任务的特性,计算环境,资源预算
6.任务的特性:
计算密集型:就是CPU的计算实打实的占用cpu。
I/O密集型:可能出现等待输出等。
7.计算密集型,在拥有N个cpu的处理器的系统上,线程池的大小为N+1时常能实现最优利用率。(这个1 ,可以应急)
8.I/O密集型,因为线程不会一直执行,所以这个空闲的时间,要交给其他的线程去执行,这样能提高利用率,所以会线程池大一些。
9.ThreadPoolExecutor:是一个灵活、稳定的线程池,允许进行各种定制(就是构造函数中又很多参数可以自己传)。
以下几个常用的概念:
基本大小:线程池中没有任务执行时候的大小,只有当工作队列满了以后才会创建超过这个数量的线程
最大大小:很容易理解就是创建的最大的数量,即使不够用也不会在创建。
存活时间:就是一个时间段,如果一个线程处于空闲的时间超过了这个值那么就标记这个线程可回收,但不会低于基本大小,也就是说创建多余了以后。
10.newCachedThreadPool 之前提及过,可以表述为:基本大小是0,存活时间为1分钟,最大值为无线大的一个线程池。
11.线程池的另一的特色就是 队列,请求提交的任务超过了处理的能力,那么就会在队列中等待。
有界队列(LinkedBlockingQueue):任务持续不断的高速提交,队列将无限增加。
无界队列(ArrayBlockingQueue):避免资源耗尽
12.SynchronousQueue,避免排队,不经过队列直接提交给线程,不是工作线程从队列中取,而是我提交一个任务,要有一个线程在来接收,要么创建线程要么拒绝任务。newCachedThreadPool就是使用这种机制。
13.当然只有任务是相互独立的设置有界的线程池和工作队列才是有意义的。不然就会产生刚开始提及的“饥饿”死锁问题。(所以任务直接有依赖请使用无界线程池 newCachedThreadPool)
14.饱和策略,上面说的有界队列,如果满了来了新的任务怎么办
终止策略是默认的策略,就是抛出RejectedExecutionException 然调用者自己实现。
15.线程工程:
线程池中创建线程是通过工厂方法创建的
领取专属 10元无门槛券
私享最新 技术干货