当有多个线程可以运行时,由线程调度器决定哪些线程将会运行,以及运行多长时间。任何一个合理的操作系统在做出这样的决定时,都会努力做到公正,但是所采用的策略却大相径庭。因此,编写良好的程序不应该依赖于这种策略的细节。任何依赖于线程调度器来达到正确性或者性能要求的程序,很有可能都是不可移植的。
要编写健壮的、响应良好的、可移植的多线程应用程序,最好的办法是确保可运行线程的平均数量不明显多余处理器的数量。这使得线程调度器没有更多的选择:它只需要运行这些可运行的线程,直到他们不再可运行为止。即使在根本不同的线程调度算法下,这些程序的行为也不会有很大的变化。注意可运行线程的数量并不等于线程的总数量,前者可能更多。在等待的线程并不是可运行的。
保持可运行线程数量尽可能少的主要方法是,让每个线程做些有意义的工作,然后等待更多有意义的工作。如果线程没有在做有意义的工作,就不应该运行。根据Executor Framework,这意味着适当地规定了线程池的大小,并且使任务保持适当地小,彼此独立。任务不应该太小,否则分配的开销也会影响到性能。
线程不应该一直处于忙-等的状态,即反复的检查一个共享对象,以等待某些事情发生。除了使程序易受到调度器的变化影响之外,忙-等这种做法也会极大地增加处理器的负担,降低了同一机器上其他进程上其他进程可以完成的有用工作量。
如果某一个程序不能工作,是因为某些线程无法像其他线程那样获得足够的CPU时间,那么,不要企图通过调用Thead.yield来“修正”该程序。你可能好不容易成功让程序能够工作,但这样得到的程序仍然是不可移植的。同一个yield调用在一个JVM实现上能提高性能,而在另一个JVM实现上却有可能会更差,在第三个JVM实现上则可能没有影响。Thread.yield没有可测试的语义。更好地解决办法是重新构造应用程序,以减少可并发运行的线程数量。
有一种相关的方法是调整线程优先级,同样有类似的警告。线程优先级是Java平台上最不可移植的特征了。通过调整某些线程的优先级来改善应用程序的响应能力,这样做并非不合理,却是不必要的,也是不可移植的。通过调整线程的优先级来解决严重的活性问题是不合理的。在你找到并修正底层的真正原因之前,这个问题可能会再次出现。
应该使用Thread.sleep(1)代替Thead.yield来进行并发测试。千万不要使用Thread.sleep(0),它会立即返回。
简而言之,不要让应用程序的正确性依赖于线程调度器。否则,结果得到的应用程序将既不健壮,也不具有可移植性。作为推论,不要依赖Thread.yield或者线程优先级。这些设施仅仅对调度器做些暗示。线程优先级可以用来提高一个已经能够正常工作的程序的服务质量,但永远不应该用来“修正”一个原本并不能工作的程序。
因篇幅问题不能全部显示,请点此查看更多更全内容