Linux JVM 是不是真的实现了线程优先级?

Posted

技术标签:

【中文标题】Linux JVM 是不是真的实现了线程优先级?【英文标题】:Do Linux JVMs actually implement Thread priorities?Linux JVM 是否真的实现了线程优先级? 【发布时间】:2010-12-12 08:12:31 【问题描述】:

编写了一个快速的 Java 程序,以每个优先级生成 10 个线程,并使用 BigDecimals 计算 pi(4*atan(1) 方法),每次 500,000 次,加入每个线程并报告运行方法的经过时间。是的,可能不是最好的例子,但保持基本。

我知道Bug4813310

在 C 中做这件事很重要,但我们可以假设在 Linux JVM 上从未设置本机优先级吗?

$uname -r && grep bogomips /proc/cpuinfo
2.4.33.3
bogomips        : 4312.26
$java -version 2>&1 |head -1
Java version "1.6.0_01"
$javac T.java && java -Xmx32m -XX:+UseThreadPriorities T
1:3112
2:2636
3:2662
4:3118
5:2870
6:3319
7:3412
8:3304
9:3299
10:3069

看起来与预期的偏差并不大!那是在一个小型虚拟 Linux 机器上。也许只是孙的?我们将尝试 IBM J9 VM:

1:4091
2:4142
3:3957
4:3905
5:3984
6:3985
7:4130
8:4055
9:3752
10:4071

相比之下,总数字看起来相当不错,但从线程优先级的角度来看,这些数字没有比例。

让我们尝试在 2.6 内核上使用较旧的 Sun JVM 进行 500k 次迭代,该 JVM 经常加载平均负载很少低于 7:

$uname -r && grep bogomips /proc/cpuinfo
2.6.9-67.ELsmp
bogomips        : 3992.93
bogomips        : 3990.00
$java -version 2>&1 |head -1
java version "1.4.2_14"
$javac T.java && java -Xmx32m -XX:+UseThreadPriorities T
1:63200
2:64388
3:62532
4:58529
5:62292
6:64872
7:64885
8:64584
9:61653
10:61575

让我们在一个只有 2.6 内核的真实平板上试用 IBM 的 J9,由于系统更大,我会将迭代次数增加到 2,000,000。

$uname -r && grep bogomips /proc/cpuinfo
2.6.9-78.ELsmp
bogomips        : 5989.03
bogomips        : 5985.03
bogomips        : 5985.01
bogomips        : 5985.02
bogomips        : 5984.99
bogomips        : 5985.02
bogomips        : 5984.99
bogomips        : 5985.02
$java -Xmx32m T # this is the IBM J9
1:1718
2:1569
3:1989
4:1897
5:1839
6:1688
7:1634
8:1552
9:2027
10:1522

一些美好的时光,但仍然没有明显的线程/进程优先级。

让我们试试 Windows 盒子。我知道 Windows 有一个相当激进的线程优先级方案。任何高于正常轶事的东西都会消耗更多。因此,让我们在每个线程中进行 900,000 次迭代:

C:\>java -version
java version "1.6.0_11"
C:\>java -Xmx32m T
1:12578
2:12625
3:11469
4:11453
5:10781
6:8937
7:10516
8:8406
9:9953
10:7391

正是我们正在寻找的东西,不是吗?

那么 Linux JVM 显然没有线程优先级? 我知道您不能真正降低 C 语言的 nice 级别,但我认为 JVM 工程师会想出如何保持一个低调的调度员。

【问题讨论】:

【参考方案1】:

这里只是在黑暗中拍摄,但在 JVM 中具有优先级线程不需要能够调整操作系统线程的优先级吗?

Linux(以及任何类 Unix 操作系统)限制了将进程更高优先级授予 root 的能力。我认为线程也会有类似的限制。

【讨论】:

没错,你可以让自己变得向上,而不是向下。但是,我认为调度线程/进程可以将其所在位置的 10 个级别向上提升。甚至可能(喘气)有一个单独的根守护进程,它会为 JVM 线程分配优先级。 我不了解 Java,但在 .NET 中,托管线程与 OS 线程不同,即 CLR 自己调度线程。因此,这对于 JVM 也应该是可能的。 我不认为 CLR 使用绿色线程。我几乎可以肯定线程调度是由操作系统完成的。 根据dotnetmonster.com/Uwe/Forum.aspx/dotnet-clr/1844/… ,您不能指望CLR 和本机线程之间的线程亲和性,尽管默认情况下CLR 似乎会这样做。【参考方案2】:

来自 JDK 8 源代码,comment reads:

////////////////////////////////////////////////////////////////////////////////
// thread priority support

// Note: Normal Linux applications are run with SCHED_OTHER policy. SCHED_OTHER
// only supports dynamic priority, static priority must be zero. For real-time
// applications, Linux supports SCHED_RR which allows static priority (1-99).
// However, for large multi-threaded applications, SCHED_RR is not only slower
// than SCHED_OTHER, but also very unstable (my volano tests hang hard 4 out
// of 5 runs - Sep 2005).
//
// The following code actually changes the niceness of kernel-thread/LWP. It
// has an assumption that setpriority() only modifies one kernel-thread/LWP,
// not the entire user process, and user level threads are 1:1 mapped to kernel
// threads. It has always been the case, but could change in the future. For
// this reason, the code should not be used as default (ThreadPriorityPolicy=0).
// It is only used when ThreadPriorityPolicy=1 and requires root privilege.

...

Later on,我们看到了:

static int prio_init() 
  if (ThreadPriorityPolicy == 1) 
    // Only root can raise thread priority. Don't allow ThreadPriorityPolicy=1
    // if effective uid is not root. Perhaps, a more elegant way of doing
    // this is to test CAP_SYS_NICE capability, but that will require libcap.so
    if (geteuid() != 0) 
      if (!FLAG_IS_DEFAULT(ThreadPriorityPolicy)) 
        warning("-XX:ThreadPriorityPolicy requires root privilege on Linux");
      

      ThreadPriorityPolicy = 0;
    
  

  if (UseCriticalJavaThreadPriority) 
    os::java_to_os_priority[MaxPriority] = os::java_to_os_priority[CriticalPriority];
  

  return 0;

...

还有subsequently:

OSReturn os::set_native_priority(Thread* thread, int newpri) 
  if ( !UseThreadPriorities || ThreadPriorityPolicy == 0 ) return OS_OK;

  int ret = setpriority(PRIO_PROCESS, thread->osthread()->thread_id(), newpri);
  return (ret == 0) ? OS_OK : OS_ERR;

所以!至少在 Sun Java 上,在 Linux 上,除非您完成了 -XX:ThreadPriorityPolicy 并且这似乎需要 root,否则您不会看到线程优先级。

【讨论】:

我也是这么想的。使用 UseThreadPriorities 切换到 root 没有任何影响。但是,作为 root 和 -XX:ThreadPriorityPolicy=1 yields: 1:3809 2:3587 3:3679 4:3223 5:2796 6:2686 7:2233 8:1895 9:1759 10:2311 看起来不错:) 给 -XX:ThreadPriorityPolicy 一个值 != 0 和 != 1 也可以让你提高线程优先级【参考方案3】:

默认的 Linux 线程调度程序策略 SCHED_OTHER 不支持优先级。或者更准确地说,它支持具有一个值的优先级设置:0。另一个所谓的“实时”策略 SCHED_FIFO 和 SCHED_RR 支持更高的优先级,但仅适用于具有超级用户权限的进程。

【讨论】:

从其他代码sn-p SCHED_OTHER // only supports dynamic priority来看这应该是什么意思 SCHED_OTHER 仅支持 niceness,这是对调度程序如何确定线程和进程优先级的提示。

以上是关于Linux JVM 是不是真的实现了线程优先级?的主要内容,如果未能解决你的问题,请参考以下文章

守护线程

OpenJDK为什么采用JVM线程和内核线程1:1的模型?

如何在linux中增加QT Gui线程优先级[重复]

Thread线程源码解析

JDK1.8 Thread 源码分析

JVM线程与Linux内核线程的映射(关系)