Java 多線程系列第 6 篇。java
這篇咱們來看看 Java 線程的優先級。windows
Thread
類中,使用以下屬性來表明優先級。設計模式
private int priority;
複製代碼
咱們能夠經過 setPriority(int newPriority)
來設置新的優先級,經過 getPriority()
來獲取線程的優先級。api
有些資料經過下面的例子就得出了一個結論:Java 線程默認優先級是 5。數組
public static void main(String[] args) {
Thread thread = new Thread();
System.out.println(thread.getPriority());
}
// 打印結果:5
複製代碼
其實這是大錯特錯的,只是看到了表面,看看下面的例子,咱們把當前線程的優先級改成 4,發現子線程 thread 的優先級也是 4。bash
public static void main(String[] args) {
Thread.currentThread().setPriority(4);
Thread thread = new Thread();
System.out.println(thread.getPriority());
}
// 打印結果:4
複製代碼
這啪啪啪打臉了,若是是線程默認優先級是 5,咱們新建立的 thread 線程,沒設置優先級,理應是 5,但實際是 4。咱們看看 Thread
初始化 priority
的源代碼。多線程
Thread parent = currentThread();
this.priority = parent.getPriority();
複製代碼
原來,線程默認的優先級是繼承父線程的優先級,上面例子咱們把父線程的優先級設置爲 4,因此致使子線程的優先級也變成 4。併發
嚴謹一點說,子線程默認優先級和父線程同樣,Java 主線程默認的優先級是 5。app
Java 中定義了 3 種優先級,分別是最低優先級(1)
、正常優先級(5)
、最高優先級(10)
,代碼以下所示。Java 優先級範圍是 [1, 10],設置其餘數字的優先級都會拋出 IllegalArgumentException
異常。異步
/** * The minimum priority that a thread can have. */
public final static int MIN_PRIORITY = 1;
/** * The default priority that is assigned to a thread. */
public final static int NORM_PRIORITY = 5;
/** * The maximum priority that a thread can have. */
public final static int MAX_PRIORITY = 10;
複製代碼
接下來講說線程優先級的做用。先看下面代碼,代碼邏輯是建立了 3000 個線程,分別是: 1000 個優先級爲 1 的線程, 1000 個優先級爲 5 的線程,1000 個優先級爲 10 的線程。用 minTimes
來記錄 1000 個 MIN_PRIORITY
線程運行時時間戳之和,用 normTimes
來記錄 1000 個 NORM_PRIORITY
線程運行時時間戳之和,用 maxTimes
來記錄 1000 個 MAX_PRIORITY
線程運行時時間戳之和。經過統計每一個優先級的運行的時間戳之和,值越小表明的就是越優先執行。咱們運行看看。
public class TestPriority {
static AtomicLong minTimes = new AtomicLong(0);
static AtomicLong normTimes = new AtomicLong(0);
static AtomicLong maxTimes = new AtomicLong(0);
public static void main(String[] args) {
List<MyThread> minThreadList = new ArrayList<>();
List<MyThread> normThreadList = new ArrayList<>();
List<MyThread> maxThreadList = new ArrayList<>();
int count = 1000;
for (int i = 0; i < count; i++) {
MyThread myThread = new MyThread("min----" + i);
myThread.setPriority(Thread.MIN_PRIORITY);
minThreadList.add(myThread);
}
for (int i = 0; i < count; i++) {
MyThread myThread = new MyThread("norm---" + i);
myThread.setPriority(Thread.NORM_PRIORITY);
normThreadList.add(myThread);
}
for (int i = 0; i < count; i++) {
MyThread myThread = new MyThread("max----" + i);
myThread.setPriority(Thread.MAX_PRIORITY);
maxThreadList.add(myThread);
}
for (int i = 0; i < count; i++) {
maxThreadList.get(i).start();
normThreadList.get(i).start();
minThreadList.get(i).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("maxPriority 統計:" + maxTimes.get());
System.out.println("normPriority 統計:" + normTimes.get());
System.out.println("minPriority 統計:" + minTimes.get());
System.out.println("普通優先級與最高優先級相差時間:" + (normTimes.get() - maxTimes.get()) + "ms");
System.out.println("最低優先級與普通優先級相差時間:" + (minTimes.get() - normTimes.get()) + "ms");
}
static class MyThread extends Thread {
public MyThread(String name) {
super(name);
}
@Override
public void run() {
System.out.println(this.getName() + " priority: " + this.getPriority());
switch (this.getPriority()) {
case Thread.MAX_PRIORITY :
maxTimes.getAndAdd(System.currentTimeMillis());
break;
case Thread.NORM_PRIORITY :
normTimes.getAndAdd(System.currentTimeMillis());
break;
case Thread.MIN_PRIORITY :
minTimes.getAndAdd(System.currentTimeMillis());
break;
default:
break;
}
}
}
}
複製代碼
執行結果以下:
# 第一部分
max----0 priority: 10
norm---0 priority: 5
max----1 priority: 10
max----2 priority: 10
norm---2 priority: 5
min----4 priority: 1
.......
max----899 priority: 10
min----912 priority: 1
min----847 priority: 5
min----883 priority: 1
# 第二部分
maxPriority 統計:1568986695523243
normPriority 統計:1568986695526080
minPriority 統計:1568986695545414
普通優先級與最高優先級相差時間:2837ms
最低優先級與普通優先級相差時間:19334ms
複製代碼
咱們一塊兒來分析一下結果。先看看第一部分,最開始執行的線程高優先級、普通優先級、低優先級都有,最後執行的線程也都有各個優先級的,這說明了:優先級高的線程不表明必定比優先級低的線程優先執行。也能夠換另外一種說法:代碼執行順序跟線程的優先級無關。看看第二部分的結果,咱們能夠發現最高優先級的 1000 個線程執行時間戳之和最小,而最低優先級的 1000 個線程執行時間戳之和最大,所以能夠得知:一批高優先級的線程會比一批低優先級的線程優先執行,即高優先級的線程大機率比低優先的線程優先得到 CPU 資源。
Java 做爲跨平臺語言,線程有 10 個等級,可是映射到不一樣操做系統的線程優先級值不同。接下來教你們怎麼在 OpenJDK
源碼中查各個操做系統中線程優先級映射的值。
setPriority0()
;private native void setPriority0(int newPriority);
複製代碼
Thread.c
代碼中找到 setPriority0()
對應的方法 JVM_SetThreadPriority
;static JNINativeMethod methods[] = {
...
{"setPriority0", "(I)V", (void *)&JVM_SetThreadPriority},
...
};
複製代碼
JVM_SetThreadPriority
找到 jvm.cpp 中對應的代碼段;JVM_ENTRY(void, JVM_SetThreadPriority(JNIEnv* env, jobject jthread, jint prio))
JVMWrapper("JVM_SetThreadPriority");
// Ensure that the C++ Thread and OSThread structures aren't freed before we operate
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 not yet started; priority pushed down when it is
Thread::set_priority(thr, (ThreadPriority)prio);
}
JVM_END
複製代碼
java_lang_Thread::set_Priority()
這段代碼,繼續找 thread.cpp 代碼中的 set_Priority()
方法;void Thread::set_priority(Thread* thread, ThreadPriority priority) {
trace("set priority", thread);
debug_only(check_for_dangling_thread_pointer(thread);)
// Can return an error!
(void)os::set_priority(thread, priority);
}
複製代碼
os::set_priority()
,接着繼續找出 os.cpp 的 set_priority()
方法;OSReturn os::set_priority(Thread* thread, ThreadPriority p) {
#ifdef ASSERT
if (!(!thread->is_Java_thread() ||
Thread::current() == thread ||
Threads_lock->owned_by_self()
|| thread->is_Compiler_thread()
)) {
assert(false, "possibility of dangling Thread pointer");
}
#endif
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_to_os_priority[p]
,接下來就是找各個操做系統下的該數組的值。好比下面是 Linux 系統的優先級值。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
};
複製代碼
好了,你們應該知道怎麼找出 Java 線程優先級 [1,10] 一一對應各個操做系統中的優先級值。下面給你們統計一下。
Java 線程優先級 | Linux | Windows | Apple | Bsd | Solaris |
---|---|---|---|---|---|
1 | 4 | THREAD_PRIORITY_LOWEST(-2) | 27 | 0 | 0 |
2 | 3 | THREAD_PRIORITY_LOWEST(-2) | 28 | 3 | 32 |
3 | 2 | THREAD_PRIORITY_BELOW_NORMAL(-1) | 29 | 6 | 64 |
4 | 1 | THREAD_PRIORITY_BELOW_NORMAL(-1) | 30 | 10 | 96 |
5 | 0 | THREAD_PRIORITY_NORMAL(0) | 31 | 15 | 127 |
6 | -1 | THREAD_PRIORITY_NORMAL(0) | 32 | 18 | 127 |
7 | -2 | THREAD_PRIORITY_ABOVE_NORMAL(1) | 33 | 21 | 127 |
8 | -3 | THREAD_PRIORITY_ABOVE_NORMAL(1) | 34 | 25 | 127 |
9 | -4 | THREAD_PRIORITY_HIGHEST(2) | 35 | 28 | 127 |
10 | -5 | THREAD_PRIORITY_HIGHEST(2) | 36 | 31 | 127 |
Windows 系統的在 OpenJDK 源碼中只找到上面的常量,值是經過微軟提供的函數接口文檔查到的,連接在這:setthreadpriority
咱們從這個表格中也能夠發現一些問題,即便咱們在 Java 代碼中設置了比較高的優先級,其實映射到操做系統的線程裏面,並不必定比設置了低優先級的線程高,頗有多是相同的優先級。看看 Solaris 操做系統 這個極端的例子,優先級 5 到 10 映射的是相同的線程等級。
回頭想一想上面的例子爲何 3000 個線程,MAX_PRIORITY
優先級的 1000 個線程會優先執行呢?由於咱們的 3 個優先級分別映射到 Windows 操做系統線程的 3 個不一樣的等級,因此纔會生效。假設將 一、五、10 改爲 五、六、7,運行結果那就不大同樣了。
最後記住:切莫把線程優先級當作銀彈,優先級高的線程不必定比優先級低的線程優先執行。
這篇線程優先級文章也告段落了,朋友們看完以爲有用麻煩幫點個在看
,推薦給身邊朋友看看,原創不易。
推薦閱讀
瞭解Java線程優先級,更要知道對應操做系統的優先級,否則會踩坑
後臺回覆『設計模式』能夠獲取《一故事一設計模式》電子書
以爲文章有用幫忙轉發&點贊,多謝朋友們!