从 Java 到 JVM 到 OS 线程的优先级

前言
Java 的线程的调度机制由 JVM 实现,假如有若干条线程,你想让某些线程拥有更长的执行时间,或某些线程分配少点执行时间,这时就涉及“线程优先级”。

优先级别
Java 把线程优先级分成10个级别,线程被创建时如果没有明确声明则使用默认优先级,JVM 将根据每个线程的优先级分配执行时间的概率。有三个常量 Thread.MIN_PRIORITY 、 Thread.NORM_PRIORITY 、 Thread.MAX_PRIORITY 分别表示最小优先级值(1)、默认优先级值(5)、最大优先级值(10)。

由于 JVM 的实现以宿主操作系统为基础,所以 Java 优先级值与各种不同操作系统的原生线程优先级必然存在某种映射关系,这样才足以封装所有操作系统的优先级提供统一优先级语义。例如1-10优先级值在 Linux 可能要与-20-19优先级值进行映射,而 Windows 系统则有9个优先级要映射。

优先级高先执行?
我们能否用优先级值的大小来控制线程的执行顺序呢?答案明显是不能的。这是因为影响线程优先级的因素有很多,包括:

不同版本的操作系统和 JVM 都可能行为不相同。

优先级对于不同操作系统调度器的意义可能不相同。

有些操作系统的调度器不支持优先级。

对于操作系统,线程的优先级存在“全局”和“本地”之分,一般不同进程的优先级相互独立。

前面提到过,不同的操作系统优先级定义的值不一样,而 Java 只定义1-10。

操作系统常常会对长时间得不到运行的线程给予增加一定的优先级。

操作系统的线程调度器可能会在线程发生等等时有一定的临时优先级调整策略。

一个例子
下面一个简单例子,两个线程每次运行的结果可能都不相同。

public class ThreadPriorityTest {

public static void main(String[] args) {
    Thread t = new MyThread();
    t.setPriority(10);
    t.setName("00");
    Thread t2 = new MyThread();
    t2.setPriority(8);
    t2.setName("11");
    t2.start();
    t.start();
}

static class MyThread extends Thread {
    public void run() {
        for (int i = 0; i < 5; i++)
            System.out.println(this.getName());
    }
}

}
setPriority方法
该方法用于设置优先级,逻辑为:

检查是否有权限访问该线程。

检查优先级值的合法性,必须在 MIN_PRIORITY 和 MAX_PRIORITY 之间。

不能超过线程组的最大优先级值。

调用 setPriority0 本地方法。

public static final int MIN_PRIORITY = 1;
public static final int NORM_PRIORITY = 5;
public static final int MAX_PRIORITY = 10;

public final void setPriority(int newPriority) {
ThreadGroup g;
checkAccess();
if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
throw new IllegalArgumentException();
}
if((g = getThreadGroup()) != null) {
if (newPriority > g.getMaxPriority()) {
newPriority = g.getMaxPriority();
}
setPriority0(priority = newPriority);
}
}

private native void setPriority0(int newPriority);
本地实现
Java 层声明的本地方法对应实现在 Thread.c 中, setPriority0 是一个注册到 JVM 中的方法,它与 JVM 中的 JVM_SetThreadPriority 函数绑定了,所以实现逻辑在 JVM_SetThreadPriority 函数里。逻辑为:

JVMWrapper("JVM_SetThreadPriority") 用于调试。

通过 MutexLocker 获取互斥锁。

转成 JVM 层使用的 oop 对象,它是 JVM 中对 Java 层 Thread 对象的描述。

设置 oop 对象的优先级属性值,这里通过 java_lang_Thread::set_priority 来设置,即 java_thread->int_field_put(_priority_offset, priority) ,这里是通过计算 oop 对象中 priority 属性存储的偏移地址,然后将值设置到该地址。

通过 java_lang_Thread::thread 获取 JavaThread 指针,即 (JavaThread*)java_thread->address_field(_eetop_offset) ,其中通过计算 eetop 偏移来获取,eetop 属于 Java 层的 Thread 类中的属性。可以这样做的原因是 JavaThread 对象维护了一个指向 oop 的指针,而 oop 也同样维护了一个指向 JavaThread 对象的指针。

最后调用 Thread::set_priority 来设置操作系统级别的线程优先级,通过调用 os::set_priority 来实现。

static JNINativeMethod methods[] = {
...
{"setPriority0", "(I)V", (void *)&JVM_SetThreadPriority},
...
};

JVM_ENTRY(void, JVM_SetThreadPriority(JNIEnv env, jobject jthread, jint prio))
JVMWrapper("JVM_SetThreadPriority");
MutexLocker ml(Threads_lock);
oop java_thread = JNIHandles::resolve_non_null(jthread);
java_lang_Thread::set_priority(java_thread, (ThreadPriority)prio);
JavaThread
thr = java_lang_Thread::thread(java_thread);
if (thr != NULL) {
Thread::set_priority(thr, (ThreadPriority)prio);
}
JVM_END
void Thread::set_priority(Thread* thread, ThreadPriority priority) {
debug_only(check_for_dangling_thread_pointer(thread);)
(void)os::set_priority(thread, priority);
}
接着看 os::set_priority 函数的实现:

首先判断优先级值的合法性。

其次是通过 java_to_os_priority 将 Java 层的优先级映射成操作系统的优先级,各种操作系统不相同,在下面“优先级映射”中细讲。

最后将调用 set_native_priority 函数设置线程优先级,各种操作系统不相同,在下面“OS线程优先级设置”中细讲。

OSReturn os::set_priority(Thread* thread, ThreadPriority p) {
if (p >= MinPriority && p <= MaxPriority) {
int priority = java_to_os_priority[p];
return set_native_priority(thread, priority);
} else {
assert(false, "Should not happen");
return OS_ERR;
}
}
优先级映射
Java 层的优先级值需要转成操作系统的优先级值,这中间存在一个映射操作,下面看怎么映射?前面说到通过 java_to_os_priority 转换,它是一个数组,这个数组一共有12个元素。下面看 Linux 和 Windows 操作系统的值:

对于Linux
Java 层的1,10分别对应 Linux 的4和-5,Linux 线程的优先级值范围是 -20到19,其中-20位最高优先级,19位最低优先级,Java 则是使用了-5到4来映射1到10的优先级。

int os::java_to_os_priority[CriticalPriority + 1] = {
19, // 0 Entry should never be used
4, // 1 MinPriority
3, // 2
2, // 3
1, // 4
0, // 5 NormPriority
-1, // 6
-2, // 7
-3, // 8
-4, // 9 NearMaxPriority
-5, // 10 MaxPriority
-5 // 11 CriticalPriority
};
对于Windows
Java 层的1和2都映射到 THREAD_PRIORITY_LOWEST ,其他也类似,连续两个值分别映射到相同值上。

int os::java_to_os_priority[CriticalPriority + 1] = {
THREAD_PRIORITY_IDLE, // 0 Entry should never be used
THREAD_PRIORITY_LOWEST, // 1 MinPriority
THREAD_PRIORITY_LOWEST, // 2
THREAD_PRIORITY_BELOW_NORMAL, // 3
THREAD_PRIORITY_BELOW_NORMAL, // 4
THREAD_PRIORITY_NORMAL, // 5 NormPriority
THREAD_PRIORITY_NORMAL, // 6
THREAD_PRIORITY_ABOVE_NORMAL, // 7
THREAD_PRIORITY_ABOVE_NORMAL, // 8
THREAD_PRIORITY_HIGHEST, // 9 NearMaxPriority
THREAD_PRIORITY_HIGHEST, // 10 MaxPriority
THREAD_PRIORITY_HIGHEST // 11 CriticalPriority
};
而 Windows 平台有如下值,可以看到并没有对全部值进行映射。

THREAD_MODE_BACKGROUND_BEGIN
THREAD_MODE_BACKGROUND_END
THREAD_PRIORITY_ABOVE_NORMAL
THREAD_PRIORITY_BELOW_NORMAL
THREAD_PRIORITY_HIGHEST
THREAD_PRIORITY_IDLE
THREAD_PRIORITY_LOWEST
THREAD_PRIORITY_NORMAL
THREAD_PRIORITY_TIME_CRITICAL
OS线程优先级设置
前面说到 JVM 通过调用 set_native_priority 函数设置操作系统的线程优先级,这个函数会根据不同的操作系统做不同处理,这里看看 Linux 和 Windows 的情况。

对于Linux
调用系统函数 setpriority 来实现,成功返回 OS_OK。

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;
}
对于Windows
调用系统函数 SetThreadPriority 来实现,成功返回 OS_OK。

OSReturn os::set_native_priority(Thread* thread, int priority) {
if (!UseThreadPriorities) return OS_OK;
bool ret = SetThreadPriority(thread->osthread()->thread_handle(), priority) != 0;
return ret ? OS_OK : OS_ERR;
}

原文地址:http://blog.51cto.com/13807145/2132540

时间: 2024-10-09 20:17:07

从 Java 到 JVM 到 OS 线程的优先级的相关文章

[Java Performance] JVM 线程调优

调整线程栈空间 当非常缺少内存时,可以调整线程使用的内存.每个线程都有一个栈,用来记录该线程的调用栈信息.线程中的栈的默认空间是有OS和JVM的版本决定的: OS 32-bit 64-bit Linux 320 KB 1 MB Mac OS N/A 1 MB Solaris Sparc 512 KB 1 MB Solaris X86 320 KB 1 MB Windows 320 KB 1 MB 当栈空间被设置的过小时,可能会因为有较长的调用栈而抛出StackOverflowError. 在64

浅析java线程和OS线程的关系

探究java线程和OS线程之间的联系 一.准备工作 1.查看linux创建线程的方法    man pthread_create 根据man的配置可知,pthread_create会创建一个线程,这个函数是Linux的函数,可以通过C或者C++调用,该函数在pthread.h中 2.查看openjdk版本, rpm -qa | grep jdk 3.卸载原始openJDK版本 rpm -e --nodeps  xxxxx 4.准备oracle jdk7/jdk8,官网可自行下载对应的linux安

Java学习之二(线程(了解) JVM GC 垃圾回收)

线程与进程(了解)→JVM→字节码→GC 一.程序 = 算法 + 数据结构(大佬) 二.程序 = 框架 + 业务逻辑(现实) 1.线程与进程.同步与异步 1.1进程是什么? 进程就是操作系统控制的基本运行单元,说白了就是Java运行程序. 1.2什么是线程? 进程中独立运行的子任务就是一个线程 1.3什么是多线程(异步)? 多线程是多个子任务进行交替执行.例如:有A.B两个任务,A先执行,再执行B,如果是单线程执行,则需要等A先执行完之后才能够执行B:而多线程的话是A与B来回交换执行.这样可以让

深入理解JVM—Java 6 JVM参数配置说明

原文地址:http://yhjhappy234.blog.163.com/blog/static/316328322011119111014657/ 使用说明< xmlnamespace prefix ="o" ns ="urn:schemas-microsoft-com:office:office" /> -XX:+<option> 启用选项 -XX:-<option> 不启用选项 -XX:<option>=<

Java 6 JVM参数选项大全(中文版)

原文来自: http://kenwublog.com/docs/java6-jvm-options-chinese-edition.htm 本文是基于最新的SUN官方文档Java SE 6 Hotspot VM Options 编写的译文.主要介绍JVM中的非稳态选项及其使用说明. 为了让读者明白每个选项的含义,作者在原文基础上补充了大量的资料.希望这份文档,对正在研究JVM参数的朋友有帮助! 非稳态选项使用说明 -XX:+<option> 启用选项 -XX:-<option> 不

JVM 内部运行线程介绍

线程 所属 说明 Attach Listener JVM           Attach Listener线程是负责接收到外部的命令,而对该命令进行执行的并且吧结果返回给发送者.通常我们会用一些命令去要求jvm给我们一些反馈信息,如:java -version.jmap.jstack等等.如果该线程在jvm启动的时候没有初始化,那么,则会在用户第一次执行jvm命令时,得到启动. Signal Dispatcher JVM         前面我们提到第一个Attach Listener线程的职

学习笔记2:java中Thread类与线程的创建

线程 是程序中的执行线程.Java 虚拟机允许应用程序并发地运行多个执行线程. 每个线程都有一个优先级,高优先级线程的执行优先于低优先级线程.每个线程都可以或不可以标记为一个守护程序.当某个线程中运行的代码创建一个新 Thread 对象时,该新线程的初始优先级被设定为创建线程的优先级,并且当且仅当创建线程是守护线程时,新线程才是守护程序. 当 Java 虚拟机启动时,通常都会有单个非守护线程(它通常会调用某个指定类的 main 方法).Java 虚拟机会继续执行线程,直到下列任一情况出现时为止:

Java多线程(二) —— 线程安全、线程同步、线程间通信(含面试题集)

一.线程安全 多个线程在执行同一段代码的时候,每次的执行结果和单线程执行的结果都是一样的,不存在执行结果的二义性,就可以称作是线程安全的. 讲到线程安全问题,其实是指多线程环境下对共享资源的访问可能会引起此共享资源的不一致性.因此,为避免线程安全问题,应该避免多线程环境下对此共享资源的并发访问. 线程安全问题多是由全局变量和静态变量引起的,当多个线程对共享数据只执行读操作,不执行写操作时,一般是线程安全的:当多个线程都执行写操作时,需要考虑线程同步来解决线程安全问题. 二.线程同步(synchr

三、java多线程核心技术(笔记)——线程的优先级

概论: 在操作系统中,线程可以划分优先级,优先级高的获得的CPU资源较多,也就是CPU优先执行优先级较高的线程.在JAVA中线程的优先级 分1~~10个10个等级.大于或者小于会报异常. 一.线程优先级具有继承性 A 线程启动 B线程,则线程B的优先级与A的是一样的.. public class MyThread1 extends Thread { @Override public void run() { System.out.println("MyThread1 run priority=&