使用线程池时一定要注意的五个点
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用线程池时一定要注意的五个点相关的知识,希望对你有一定的参考价值。
参考技术A很多场景下应用程序必须能够处理一系列传入请求,简单的处理方式是通过一个线程顺序的处理这些请求,如下图:
单线程策略的优势和劣势都非常明显:
优势:设计和实现简单;劣势:这种方式会带来处理效率的问题,单线程的处理能力是有限,不能发挥多核处理器优势。
在这种场景下我们就需要考虑并发,一个简单的并发策略就是Thread-Per-Message模式,即为每个请求使用一个新的线程。
Thread-Per-Message策略的优势和劣势也非常明显:
优势:设计和实现比较简单,能够同时处理多个请求,提升响应效率;
劣势:主要在两个方面
1.资源消耗 引入了在串行执行中所没有的开销,包括线程创建和调度,任务处理,资源分配和回收以及频繁上下文切换所需的时间和资源。2.安全
有没有一种方式可以并发执行又可以克服Thread-Per-Message的问题?
采用线程池的策略,线程池通过控制并发执行的工作线程的最大数量来解决Thread-Per-Message带来的问题。可见下图,请求来临时先放入线程池的队列
线程池可以接受一个Runnable或Callable<T>任务,并将其存储在临时队列中,当有空闲线程时可以从队列中拿到一个任务并执行。
反例(使用 Thread-Per-Message 策略)
正例(使用 线程池 策略)
JAVA 中(JDK 1.5+)线程池的种类:
程序不能使用来自有界线程池的线程来执行依赖于线程池中其他任务的任务。
有两个场景:
要缓解上面两个场景产生的问题有两个简单的办法:
真正解决此类方法还是需要梳理线程池执行业务流程,不要在有界线程池中执行相互依赖的任务,防止出现竞争和死锁。
向线程池提交的任务需要支持中断。从而保证线程可以中断,线程池可以关闭。线程池支持 java.util.concurrent.ExecutorService.shutdownNow() 方法,该方法尝试停止所有正在执行的任务,停止等待任务的处理,并返回等待执行的任务的列表。
但是 shutdownNow() 除了尽力尝试停止处理主动执行的任务之外不能保证一定能够停止。例如,典型的实现是通过Thread.interrupt()来停止,因此任何未能响应中断的任务可能永远不会终止,也就造成线程池无法真正的关闭。
反例:
正例:
线程池中的所有任务必须提供机制,如果它们异常终止,则需要通知应用程序.
如果不这样做不会导致资源泄漏,但由于池中的线程仍然被会重复使用,使故障诊断非常困难或不可能。
在应用程序级别处理异常的最好方法是使用异常处理。异常处理可以执行诊断操作,清理和关闭Java虚拟机,或者只是记录故障的详细信息。
也就是说在线程池里执行的任务也需要能够抛出异常并被捕获处理。
任务恢复或清除操作可以通过重写 java.util.concurrent.ThreadPoolExecutor 类的 afterExecute() 钩子来执行。
当任务通过执行其 run() 方法中的所有语句并且成功结束任务,或者由于异常而导致任务停止时,将调用此钩子。
可以通过自定义 ThreadPoolExecutor 服务来重载 afterExecute()钩子。
还可以通过重载 terminated() 方法来释放线程池获取的资源,就像一个finally块。
反例:
任务意外终止时作为一个运行时异常,无法通知应用程序。此外,它缺乏恢复机制。因此,如果Task抛出一个NullPointerException ,异常将被忽略。
正例:
另外一种方式是使用 ExecutorService.submit() 方法(代替 execute() 方法)将任务提交到线程池并获取 Future 对象。
当通过 ExecutorService.submit() 提交任务时,抛出的异常并未到达未捕获的异常处理机制,因为抛出的异常被认为是返回状态的一部分,因此被包装在ExecutionException ,并由Future.get() 返回。
java.lang.ThreadLocal 类提供线程内的本地变量。根据Java API
ThreadLocal对象需要关注那些对象被线程池中的多个线程执行的类。
线程池缓存技术允许线程重用以减少线程创建开销,或者当创建无限数量的线程时可以降低系统的可靠性。
当 ThreadLocal 对象在一个线程中被修改,随后变得可重用时,在重用的线程上执行的下一个任务将能看到该线程上执行过的上一个任务修改的ThreadLocal 对象的状态。
所以要在使用线程池时重新初始化的ThreadLocal对象实例。
反例:
DiaryPool类创建了一个线程池,它可以通过一个共享的无界的队列来重用固定数量的线程。
在任何时候,不超过numOfThreads个线程正在处理任务。如果在所有线程都处于活动状态时提交其他任务,则 它们在队列中等待,直到线程可用。
当线程循环时,线程的线程局部状态仍然存在。
下表显示了可能的执行顺序:
时间任务线程池提交方法日期1t11doSomething1()星期五2t22doSomething2()星期一3t31doSomething3()星期五
在这个执行顺序中,期望从doSomething2() 开始的两个任务( t 2和t 3 doSomething2() 将当天视为星 期一。然而,因为池线程1被重用,所以t 3观察到星期五。
解决方案(try-finally条款)
符合规则的方案removeDay() 方法添加到Diary类,并在try‐finally 块中的实现doSomething1() 类的doSomething1() 方法的语句。finally 块通过删除当前线程中的值来恢复threadlocal类型的days对象的初始状态。
如果threadlocal变量再次被同一个线程读取,它将使用initialValue()方法重新初始化 ,除非任务已经明确设置了变量的值。这个解决方案将维护的责任转移到客户端( DiaryPool ),但是当Diary类不能被修改时是一个好的选择。
解决方案(beforeExecute())
使用一个自定义ThreadPoolExecutor 来扩展 ThreadPoolExecutor并覆盖beforeExecute() 方法。beforeExecute() 方法在Runnable 任务在指定线程中执行之前被调用。该方法在线程 “t” 执行任务 “r” 之前重新初始化 threadlocal 变量。
做网站seo需要特别注意的五个方面
做网站seo需要特别注意的五个方面
不管我们是不是承认,没有好的排名很多时候不是搜索引擎抽风或者如何,还是我们自己没有做好的原因,本文我就和大家分享一下得易搜信息网每天做网站seo需要特别注意的五个方面吧。
1、外链数量不够
许多行业,外链数量是不够的,不管算法如何更新,其实都离不开内链、外链、用户体验度三大方面,因此不要因为百度说需要原创内容,出了一个外链工具,就不发外链,天天写原创,其实是不对的,外链也是需要保持一定的数量。
2、外链质量太差
有些网站有大量的外链的支持,外链数量比同行的网站多几倍.但是,排名还不如他们好,这是为什么呢?相信很多站长也想到这个向题.这是因为外链质量比他们差。我们都知道,可以很简单的得到很多垃圾外链,然而,对于这个外链其质是质是很低,因此,我们不仅要追求外链数量,更重要的是质量。
3、友链
我们都知道,交换友情链接,这将有肋于我们的网站之间的权重传递,帮助我们提高排名。然而,如果我们的链接是不正常或降权的情况,在某些情况下,则必然将涉及到我们的网站,所以我们必须定期网站友链检查,以避免一些不必要的麻烦。
4、用户的体验
随着搜索引擎的发展,除了内容和外链的影响,用户体验作为一个基本条件是一个好的排名,所以当我们使用相同的网站外链数量的前提下,排名或比其他网站更好,我们需要考虑自己的网站用户体验如何,这是非常关键的。
5、诱导的链接
不少网站诱导试链接的方法来促进自己的网站访问量和外链资源,所以也确实可以让我们得到外链,但是,当我们这样忽略了垃圾外链是不是对于网站有不好的效果,尤发表许多国家的一些词汇,禁止,虽然我们的网站可能没有这样的内容,搜索引擎在诱导链接的指导下,错误地认为,这样的信息和我们网站相关,导致我们的排名不好不够,甚至降权被K等不必要的麻烦。
以上是关于使用线程池时一定要注意的五个点的主要内容,如果未能解决你的问题,请参考以下文章
ThreadLocal遇到线程池时, 各线程间的数据会互相干扰, 串来串去