Java线程优先级无效

时间:2012-08-20 13:27:50

标签: java windows multithreading java-7

这是关于线程优先级的测试。 代码来自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。 这有关系吗?

12 个答案:

答案 0 :(得分:14)

  

Java线程优先级无效

线程优先级适用于大多数操作系统,但它们通常影响很小。优先级有助于仅对运行队列中的线程进行排序,并且不会更改线程在任何主要线程中运行的频率,除非您在每个线程中执行大量CPU。

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

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

即使优先级确实有助于为一个线程提供比其他线程更多的CPU,但线程程序受race conditions的约束,这有助于为执行注入大量随机性。然而,您应该看到的是,最优先级线程更可能比其余线程更频繁地发出其0消息。如果您将优先级添加到println(),那么在多次运行中这应该是显而易见的。

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

  

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

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

答案 1 :(得分:12)

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

  

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

“实践中的Java并发”一书也写到

  

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

答案 2 :(得分:9)

线程优先级不保证执行顺序。它在资源有限时发挥作用。如果系统由于内存或CPU而遇到约束,则优先级较高的线程将首先运行。假设您有足够的系统资源(对于一个简单的程序和您发布的系统资源,我会假设这样),那么您就不会有任何系统限制。这是一篇博文(不是我的帖子),我发现它提供了有关它的更多信息:Why Thread Priority Rarely Matters

答案 3 :(得分:7)

让我们保持简单,直接找到源......

  

每个帖子都有优先权。有竞争加工时   资源,具有较高优先级的线程通常在执行   对优先级较低的线程的偏好。 这种偏好不是,   但是,保证最高优先级的线程永远是   运行,并且线程优先级不能用于可靠地实现   相互排斥。

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

另外......

  

虽然Java中存在线程优先级,但许多引用都是状态   JVM将始终选择一个最高优先级的线程   对于调度[52,56,89],目前不能保证   Java语言或虚拟机规范[53,90]。的优先级   只是调度程序的提示 [127,第227页]。

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

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

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

答案 4 :(得分:5)

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

实际上,它不仅与Java或JVM相关。大多数非实时操作系统仅以建议的方式使用线程优先级(托管或非托管)。

Jeff Atwood在这个主题上有一篇好文章:“Thread Priorities are Evil

  

这是问题所在。如果有人开始做“cond”的工作   true在较低优先级的线程(生产者)上,然后是时间   该程序是发出此优先级的高优先级线程   纺纱(消费者)被安排,消费者将饿死   生产者完全。这是一场经典的比赛。即使有   在那里明确睡眠,发出它不允许生产者   被安排,因为它的优先级较低。消费者只会   永远旋转,除非一个免费的CPU打开,制作人永远不会   生产。糟糕!

答案 5 :(得分:3)

正如其他答案中所提到的,线程优先级比严格规则的定义更具提示性。

尽管如此,即使优先考虑严格(呃)方式,您的测试设置也不会导致您描述为“预期”的结果。您首先创建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倍并将低prio线程数增加10倍,我得到一个预期的结果,即高prio线程先于某个因子完成(取决于本机CPU线程的数量。)

答案 6 :(得分:1)

我相信操作系统可以自由地忽略Java线程优先级。

答案 7 :(得分:1)

线程优先级是一个提示(可以忽略),这样当你有100%的CPU时,操作系统知道你想要一些线程而不是其他线程。必须在线程启动之前设置线程优先级,否则可以忽略它。

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

答案 8 :(得分:1)

需要考虑的几件事情:

答案 9 :(得分:0)

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

答案 10 :(得分:0)

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

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

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

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

答案 11 :(得分:0)

高优先级线程不会停止低优先级线程,直到完成。高优先级也不能使某些事情变得更快,如果可以,我们总是将所有事物都设置为高优先级。低优先级不会使事情变慢,或者我们永远不会使用它。

据我所知,您误会了系统的其余部分应该处于空闲状态,只是让最高优先级的线程工作而浪费了系统的其余部分。