跟我学(Effective Java 2)第72条:不要依赖于线程调度器

第72条:不要依赖于线程调度器

当有多个线程可以运行时,由线程调度器(thread scheduler)决定哪些线程将会运行,以及运行多长时间。任何一个合理的操作系统在做出这样的决定时,都会努力做到公正,但是所采用的策略却大相径庭。因此,编写良好的程序不应该依赖于这种策略的细节。任何依赖于线程调度器来达到正确性或者性能要求的程序,很有可能都是不可移植的。

要编写健壮的、响应良好的、可移植的多线程应用程序,最好的办法是确保可运行线程的平均数量不明显多余处理器的数量。这使得线程调度器没有更多的选择:它只需要运行这些可运行的线程,直到他们不再可运行为止。即使在根本不同的线程调度算法下,这些程序的行为也不会有很大的变化。注意可运行线程的数量并不等于线程的总数量,前者可能更多。在等待的线程并不是可运行的。

保持可运行线程数量尽可能少的主要方法是,让每个线程做些有意义的工作,然后等待更多有意义的工作。如果线程没有在做有意义的工作,就不应该运行。根据Executor Framework,这意味着适当地规定了线程池的大小,并且使任务保持适当地小,彼此独立。任务不应该太小,否则分配的开销也会影响到性能。

线程不应该一直处于忙-等(busy-wait)的状态,即反复的检查一个共享对象,以等待某些事情发生。除了使程序易受到调度器的变化影响之外,忙-等这种做法也会极大地增加处理器的负担,降低了同一机器上其他进程上其他进程可以完成的有用工作量。

如果某一个程序不能工作,是因为某些线程无法像其他线程那样获得足够的CPU时间,那么,不要企图通过调用Thead.yield来“修正”该程序。你可能好不容易成功让程序能够工作,但这样得到的程序仍然是不可移植的。同一个yield调用在一个JVM实现上能提高性能,而在另一个JVM实现上却有可能会更差,在第三个JVM实现上则可能没有影响。Thread.yield没有可测试的语义(testable semantic)。更好地解决办法是重新构造应用程序,以减少可并发运行的线程数量。

有一种相关的方法是调整线程优先级(thread priority),同样有类似的警告。线程优先级是Java平台上最不可移植的特征了。通过调整某些线程的优先级来改善应用程序的响应能力,这样做并非不合理,却是不必要的,也是不可移植的。通过调整线程的优先级来解决严重的活性问题是不合理的。在你找到并修正底层的真正原因之前,这个问题可能会再次出现。

Thread.yield的唯一用途是测试期间人为的增加程序的并发性。通过探查程序中更大部分的状态空间,可以发现一些隐蔽的Bug。这种方法曾经经常奏效,但从来都不能保证一定可行。所以,应该使用Thread.sleep(1)代替Thead.yield来进行并发测试。千万不要使用Thread.sleep(0),它会立即返回。

简而言之,不要让应用程序的正确性依赖于线程调度器。否则,结果得到的应用程序将既不健壮,也不具有可移植性。作为推论,不要依赖Thread.yield或者线程优先级。这些设施仅仅对调度器做些暗示。线程优先级可以用来提高一个已经能够正常工作的程序的服务质量,但永远不应该用来“修正”一个原本并不能工作的程序。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值