瞭解Java執行緒優先順序,更要知道對應作業系統的優先順序,不然會踩坑
Java 多執行緒系列第 6 篇。
這篇我們來看看 Java 執行緒的優先順序。
Java 執行緒優先順序
Thread
類中,使用如下屬性來代表優先順序。
private int priority;
我們可以通過 setPriority(int newPriority)
來設定新的優先順序,通過 getPriority()
來獲取執行緒的優先順序。
有些資料通過下面的例子就得出了一個結論:Java 執行緒預設優先順序是 5。
public static void main(String[] args) { Thread thread = new Thread(); System.out.println(thread.getPriority()); } // 列印結果:5
其實這是大錯特錯的,只是看到了表面,看看下面的例子,我們把當前執行緒的優先順序改為 4,發現子執行緒 thread 的優先順序也是 4。
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。
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 資源。
各作業系統中真有 10 個執行緒等級麼?
Java 作為跨平臺語言,執行緒有 10 個等級,但是對映到不同作業系統的執行緒優先順序值不一樣。接下來教大家怎麼在 OpenJDK
原始碼中查各個作業系統中執行緒優先順序對映的值。
- 看到 Thread 原始碼,設定執行緒優先順序最終呼叫了本地方法
setPriority0()
;
private native void setPriority0(int newPriority);
- 接著我們在 OpenJDK 的
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
- 根據第 3 步中的程式碼,我們可以發現關鍵是
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 個不同的等級,所以才會生效。假設將 1、5、10 改成 5、6、7,執行結果那就不大一樣了。
最後記住:切莫把執行緒優先順序當做銀彈,優先順序高的執行緒不一定比優先順序低的執行緒優先執行。
這篇執行緒優先順序文章也告段落了,朋友們看完覺得有用麻煩幫點個在看
,推薦給身邊朋友看看,原創不易。
推薦閱讀
瞭解Java執行緒優先順序,更要知道對應作業系統的優先順序,不然會踩坑
執行緒最最基礎的知識
老闆叫你別阻塞了
吃個快餐都能學到序列、並行、併發
泡一杯茶,學一學同異步
程序知多少?
設計模式看了又忘,忘了又看?
後臺回覆『設計模式』可以獲取《一故事一設計模式》電子書
覺得文章有用幫忙轉發&點贊,多謝朋友們!
相關推薦
瞭解Java執行緒優先順序,更要知道對應作業系統的優先順序,不然會踩坑
Java 多執行緒系列第 6 篇。 這篇我們來看看 Java 執行緒的優先順序。 Java 執行緒優先順序 Thread 類中,使用如下屬性來代表優先順序。 private int priority; 我們可以通過 setPriority(int newPriority) 來設定新的優先順序,通過 g
Java執行緒的優先順序從1到10級別,值越大優先順序越高
package cn.com.secn.thead; //第一種方案 class MyThead implements Runnable { public void run() { for (int i = 1; i <= 10; i++) { Sy
Java-執行緒池專題(什麼是執行緒池,如何使用,為什麼要用)
1、什麼是執行緒池: java.util.concurrent.Executors提供了一個 java.util.concurrent.Executor介面的實現用於建立執行緒池 多執行緒技術主要解決處理器單元內多個執行緒執行的問題,它可以顯著減少處理器單元的閒置時間,增
java執行緒總結--synchronized關鍵字,原理以及相關的鎖
在多執行緒程式設計中,synchronized關鍵字非常常見,當我們需要進行“同步”操作時,我們很多時候需要該該關鍵字對程式碼塊或者方法進行鎖定。被synchronized鎖定的程式碼塊,只能同時有一條執行緒訪問該程式碼塊。 上面是很多人的認識,當然也是我之前對synchronized關鍵字的淺
高併發程式設計系列:4種常用Java執行緒鎖的特點,效能比較、使用場景
高併發程式設計系列:4種常用Java執行緒鎖的特點,效能比較、使用場景 http://youzhixueyuan.com/4-kinds-of-java-thread-locks.html 在Java併發程式設計中,經常遇到多個執行緒訪問同一個 共享資源 ,這時候作為開發者
4種常用Java執行緒鎖的特點,效能比較、使用場景
文章目錄 多執行緒的緣由 多執行緒併發面臨的問題 4種Java執行緒鎖(執行緒同步) Java執行緒鎖總結 多執行緒的緣由 在出現了程序之後,作業系統的效能得到了大大的提升。雖然程序的出現解決了作業系統的併
Java 併發程式設計系列之帶你瞭解多執行緒
早期的計算機不包含作業系統,它們從頭到尾執行一個程式,這個程式可以訪問計算機中的所有資源。在這種情況下,每次都只能執行一個程式,對於昂貴的計算機資源來說是一種嚴重的浪費。 作業系統出現後,計算機可以執行多個程式,不同的程式在單獨的程序中執行。作業系統負責為各個獨
Java執行緒與執行緒安全,開啟多執行緒及每執行緒迴圈10次對類進行輸出測試
最近看到執行緒問題,emmm~腦闊回想到計算機作業系統貌似又講,不過上課睡覺覺去啦哈哈哈,java課老師莫得講~ 然歸正傳,今對執行緒進行查閱及測試,做一下筆記,有錯之處還請指出,謝謝~上程式碼之前呢先說一哈前傳 執行緒是程序中的最小執行單位: 手機呢會有很多單獨
java執行緒池技術,經典易懂
1.為什麼要使用執行緒池 在java中,如果每個請求到達就建立一個新執行緒,開銷是相當大的。在實際使用中,伺服器在建立和銷燬執行緒上花費的時間和消耗的系統資源都相當大,甚至可能要比在處理實際的使用者請求的時間和資源要多的多。除了建立和銷燬執行緒的開銷之外,活動的執行緒也需要
java執行緒池ThreadPoolExecutor和阻塞佇列BlockingQueue,Executor, ExecutorService
ThreadPoolExecutor 引數最全的建構函式 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
深入理解Java執行緒池(1):ThreadPoolExecutor整體流程梳理,建立worker相關方法
執行緒池作為一個執行緒的容器,主要的作用就是防止頻繁建立執行緒,節省時間資源和cpu資源。雖然一定程度上佔用了記憶體,但實際情況下利遠遠大於弊。 構造方法 public ThreadPoolExecutor( int corePoolSize, //核
(四)java 執行緒,執行緒池的使用
為什麼要使用執行緒池? 建立執行緒是簡單的,但啟動後的執行緒猶如脫繮野馬,難於管理,特別是多執行緒使用場景,執行緒之間的互相競爭,可能使 cpu 花費更多時間在各個執行緒之間切換,而且執行緒結束後的回收由垃圾回收控制,你不知道工作結束的執行緒還會存活多久,是否持有著什麼資源。而
java執行緒優先順序和守護執行緒的概念
執行緒優先順序和守護執行緒的概念 優先順序 守護執行緒 守護執行緒與使用者執行緒區別 參考文獻 優先順序 java的執行緒優先順序總共有10個級別,從1到10,1為優先順序最小,10為最大。通過執行緒的setPriority(in
Java執行緒學習筆記(四)-執行緒沉睡,喚醒,連線
執行緒沉睡(sleep)並不會讓執行緒釋放它所持有的同步鎖,而且在這期間也不會阻礙其他執行緒的執行。喚醒(interrupt)可以將沉睡或阻塞的執行緒喚醒。 執行緒沉睡:執行緒沉睡可以使當前執行緒沉睡
JAVA執行緒有哪些狀態,這些狀態之間是如何轉化的?
新建(new):新建立了一個執行緒物件。可執行(runnable):執行緒物件建立後,其他執行緒(比如main執行緒)呼叫了該物件的start()方法。該狀態的執行緒位於可執行執行緒池中,等待被執行緒排程選中,獲取cpu 的使用權 。執行(running):可執行狀態(run
JAVA執行緒池--Executors之什麼是執行緒池,為什麼使用執行緒池以及執行緒池的使用
1. 為什麼需要執行緒池? 多執行緒技術主要解決處理器單元內多個執行緒執行的問題,它可以顯著減少處理器單元的閒置時間,增加處理器單元的吞吐能力。 假設一個伺服器完成一項任務所需時間為:T1 建立執行緒時間,T2 線上程中執行任務的時間,T
Java執行緒池詳解,看這篇就夠了!
構造一個執行緒池為什麼需要幾個引數?如果避免執行緒池出現OOM?Runnable和Callable的區別是什麼?本文將對這些問題一一解答,同時還將給出使用執行緒池的常見場景和程式碼片段。 基礎知識 Executors建立執行緒池 Java中建立執行緒池很簡單,只需要呼叫Execu
【本人禿頂程式設計師】Java執行緒池詳解,看這篇就夠了!
←←←←←←←←←←←← 快!點關注!!! 構造一個執行緒池為什麼需要幾個引數?如果避免執行緒池出現OOM?Runnable和Callable的區別是什麼?本文將對這些問題一一解答,同時還將給出使用執行緒池的常見場景和程式碼片段。 基礎知識 Executors建立執行緒池 J
Java執行緒池的使用方式,核心執行原理、以及注意事項
Java執行緒池的使用方式,核心執行原理、以及注意事項 執行緒池的緣由 執行緒池的處理流程 執行緒池的使用(ThreadPoolExecutor) 執行緒池的注意事項 執行緒池的緣由 java中為了提高併發度,可以使用多執行緒共同
java 執行緒 join(wait) 後,是如何喚醒
Join 方法:本質上還是根據wait方法實現的。分析join原始碼發現join方法本身是使用了synchronized修飾符的。是加在方法上面的,意味著。 獲取了當前物件的鎖,然後繼續發現裡面的程式碼呼叫了wait。意味著我們先鎖,再釋放,等待喚醒,什麼情況下被喚醒呢: