14

这是一个关于线程优先级的测试。代码来自 Thinking in Java p.809

import java.util.concurrent.*;

public class SimplePriorities implements Runnable {
    private int countDown = 5;
    private volatile double d; // No optimization
    private int priority;

    public SimplePriorities(int priority) {
        this.priority = priority;
    }

    public String toString() {
        return Thread.currentThread() + ": " + countDown;
    }

    public void run() {
        Thread.currentThread().setPriority(priority);
        while (true) {
            // An expensive, interruptable operation:
            for (int i = 1; i < 10000000; i++) {
                d += (Math.PI + Math.E) / (double) i;
                if (i % 1000 == 0)
                    Thread.yield();
            }
            System.out.println(this);
            if (--countDown == 0)
                return;
        }
    }

    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool();
        for (int i = 0; i < 5; i++)
            exec.execute(new SimplePriorities(Thread.MIN_PRIORITY));
        exec.execute(new SimplePriorities(Thread.MAX_PRIORITY));
        exec.shutdown();
    }
}

我想知道为什么我不能得到像这样的常规结果:

Thread[pool-1-thread-6,10,main]: 5 
Thread[pool-1-thread-6,10,main]: 4 
Thread[pool-1-thread-6,10,main]: 3 
Thread[pool-1-thread-6,10,main]: 2 
Thread[pool-1-thread-6,10,main]: 1 
Thread[pool-1-thread-3,1,main]: 5 
Thread[pool-1-thread-2,1,main]: 5 
Thread[pool-1-thread-1,1,main]: 5 
Thread[pool-1-thread-5,1,main]: 5 
Thread[pool-1-thread-4,1,main]: 5 
...

但随机结果(每次我运行它都会改变):

Thread[pool-1-thread-2,1,main]: 5
Thread[pool-1-thread-3,1,main]: 5
Thread[pool-1-thread-4,1,main]: 5
Thread[pool-1-thread-2,1,main]: 4
Thread[pool-1-thread-3,1,main]: 4
Thread[pool-1-thread-1,1,main]: 5
Thread[pool-1-thread-6,10,main]: 5
Thread[pool-1-thread-5,1,main]: 5
...

我使用 i3-2350M 2C4T CPU 和 Win 7 64 位 JDK 7。这有关系吗?

4

12 回答 12

22

Java 线程优先级没有影响

线程优先级是高度特定于操作系统的,并且在许多操作系统上通常影响很小。优先级仅有助于对运行队列中的线程进行排序,并且不会改变线程以任何主要方式运行的频率,除非您在每个线程中执行大量 CPU。

您的程序看起来使用大量 CPU,但除非您的内核数少于线程数,否则通过设置线程优先级您可能看不到输出顺序的任何变化。如果有空闲 CPU,那么即使是较低优先级的线程也会被调度运行。

此外,线程永远不会饿死。在这种情况下,即使是较低优先级的线程也会有时间经常运行。您应该看到较高优先级的线程被给予时间片以更频繁地运行,但这并不意味着较低优先级的线程将等待它们完成后再运行自己。

即使优先级确实有助于为一个线程提供比其他线程更多的 CPU,线程程序也会受到竞争条件的影响,这有助于为其执行注入大量随机性。0然而,您应该看到的是,最大优先级线程比其他线程更有可能更频繁地吐出它的消息。如果您将优先级添加到println(),这应该在多次运行后变得明显。

同样重要的是要注意编写 IOSystem.out.println(...)synchronized方法,这将极大地影响线程的交互方式以及不同的线程相互阻塞的方式。此外,Thread.yield();取决于操作系统如何进行线程调度,它可以是无操作的。

但随机结果(每次我运行它都会改变):

正确的。线程程序的输出很少是“完美的”,因为根据定义,线程是异步运行的。我们希望输出是随机的,因为我们希望线程彼此独立地并行运行。这就是他们的力量。如果您期望一些精确的输出,那么您不应该使用线程。

于 2012-08-20T13:34:26.847 回答
14

线程优先级取决于实现。特别是在 Windows 中:

当所有线程都在竞争 CPU 时,线程优先级不是很有意义。(来源

《Java Concurrency in Practice》一书还说

避免使用线程优先级的诱惑,因为它们会增加平台依赖性并可能导致活性问题。大多数并发应用程序可以为所有线程使用默认优先级。

于 2012-08-20T13:34:50.200 回答
12

Thread priority does not guarantee execution order. It comes into play when resources are limited. If the System is running into constraints due to memory or CPU, then the higher priority threads will run first. Assuming that you have sufficient system resources (which I would assume so for a simple program and the system resources you posted), then you will not have any system constraints. Here is a blog post (not my post) I found that provides more information about it: Why Thread Priority Rarely Matters.

于 2012-08-20T13:38:25.860 回答
9

让我们保持简单,直接进入源...

每个线程都有一个优先级。当存在处理资源竞争时,优先级较高的线程通常优先于优先级较低的线程执行。然而,这种偏好并不能保证最高优先级的线程将始终运行,并且线程优先级不能用于可靠地实现互斥。

  • 来自Java 语言规范(第 2 版)第445 页。

还 ...

尽管 Java 中存在线程优先级,并且许多参考文献指出 JVM 将始终选择最高优先级线程之一进行调度 [52, 56, 89],但目前 Java 语言或虚拟机规范无法保证这一点 [53, 90] . 优先级只是对调度程序的提示[127,第 227 页]。

  • 摘自测试并发 Java 组件(博士论文,2005 年) p。62.

  • 参考 127,第 227 页(摘自上面的节选)来自组件软件:超越面向对象的编程(C. Szyperski 着),Addison Wesley,1998。

简而言之,不要依赖线程优先级

于 2013-08-19T10:42:36.923 回答
5

线程优先级只是对 OS 任务调度程序的提示。任务调度器只会尝试将更多资源分配给具有更高优先级的线程,但是没有明确的保证。

事实上,它不仅与 Java 或 JVM 有关。大多数非实时操作系统仅以暗示方式使用线程优先级(托管或非托管)。

Jeff Atwood 有一篇关于该主题的好帖子:“ Thread Priorities are Evil

这就是问题所在。如果有人开始在较低优先级线程(生产者)上使“cond”为真的工作,然后程序的时间安排使得发出此旋转的较高优先级线程(消费者)被调度,消费者将完全饿死制片人。这是一场经典的比赛。即使那里有一个明确的睡眠,发出它也不允许生产者被安排,因为它的优先级较低。消费者将永远旋转,除非空闲 CPU 开放,否则生产者将永远不会生产。哎呀!

于 2012-08-20T13:34:38.083 回答
4

正如其他答案中提到的,线程优先级更像是一个提示,而不是严格规则的定义。

也就是说,即使以严格(更)的方式考虑优先级,您的测试设置也不会导致您描述为“预期”的结果。您首先创建 5 个具有同等低优先级的线程和一个具有高优先级的线程。

  • 您使用的 CPU (i3) 有 4 个本机线程。因此,即使高优先级意味着线程不间断地运行(这是不正确的),低优先级线程将获得 3/4 的 CPU 功率(假设没有其他任务正在运行)。此 CPU 功率分配给 5 个线程,因此低优先级线程将以 4 * 3/4 * 1/5 = 高优先级线程速度的 3/5 倍运行。高优先级线程完成后,低优先级线程以高优先级线程的 4/5 速度运行。

  • 您在高优先级线程之前启动低优先级线程。因此他们开始得早一点。我希望在大多数系统中,“优先级”不会实现到纳秒级。所以操作系统将允许一个线程运行“稍长一点”,直到它切换到另一个线程(以减少切换成本的影响)。因此,结果很大程度上取决于如何实现切换以及任务有多大。如果任务很小,则可能不会发生切换,并且在您的示例中,所有低优先级线程都首先完成。

  • 这些计算假设高优先级和低优先级被解释为极端情况。事实上,优先级更像是“在 m 例中的 n 例中更喜欢这个”,带有变量 n 和 m。

  • 您的代码中有一个 Thread.yield !这会将执行传递给另一个线程。如果你这样做太频繁,它将导致所有线程获得相同的 CPU 能力。

因此,我不希望您在问题中提到的输出,即高优先级线程首先完成,然后低优先级线程完成。

确实:使用 Thread.yield 行,我得到每个线程获得相同 CPU 时间的结果。如果没有 Thread.yield 行并将计算次数增加 10 倍并将低优先级线程的数量增加 10 倍,我得到了预期的结果,即高优先级线程提前完成了一些因素(其中取决于本机 CPU 线程的数量)。

于 2013-08-19T07:13:34.173 回答
1

需要考虑的几件事:

于 2013-08-19T07:27:52.810 回答
1

线程优先级是一个提示(可以忽略),当您拥有 100% 的 CPU 时,操作系统知道您希望优先使用某些线程而不是其他线程。线程优先级必须在线程启动之前设置,否则可以忽略。

在 Windows 上,您必须是管理员才能设置线程优先级。

于 2012-08-20T19:27:42.953 回答
1

我相信操作系统可以随意忽略 Java 线程优先级。

于 2012-08-20T13:32:25.130 回答
0

您必须了解不同的操作系统以不同的方式处理线程优先级。例如,Windows 使用基于抢占式时间片的调度程序,它为更高优先级的线程提供更大的时间片,而其他一些操作系统(非常旧的操作系统)使用非抢占式调度程序,其中更高优先级的线程完全在低优先级线程之前执行除非更高优先级的线程进入睡眠状态或执行一些 IO 操作等。

这就是为什么不能保证较高优先级线程完全执行的原因,它实际上比低优先级线程执行的时间更长,这就是为什么您的输出以这种方式排序的原因。

要了解 Windows 如何处理多线程,请参阅以下链接:

https://docs.microsoft.com/en-us/windows/win32/procthread/multitasking

于 2019-09-28T19:34:02.753 回答
0

高优先级线程在完成之前不会停止低优先级线程。高优先级也不能使某些东西更快,如果确实如此,我们总是将所有东西都设为高优先级。低优先级不会让事情变慢,否则我们永远不会使用它。

据我了解,您误解了系统的其余部分应该空闲,只让最高优先级的线程工作,而系统的其余容量被浪费。

于 2020-02-16T14:55:48.140 回答
0

某些操作系统没有为线程优先级提供适当的支持。

于 2016-03-30T04:08:44.877 回答