1、線程運行原理
1.1 棧與棧幀??
Java Virtual Machine Stacks (Java 虛擬機棧 JVM)
我們都知道 JVM 中由堆、棧、方法區所組成,其中棧內存是給誰用的呢?其實就是線程,每個線程啟動后,虛擬機就會為其分配一塊棧內存。
-
每個棧由多個棧幀(Frame)組成,對應著每次方法調用時所占用的內存
-
每個線程只能有一個活動棧幀,對應著當前正在執行的那個方法
單線程示例代碼
public class TestFrames {
? ?public static void main(String[] args) {
? ? ? ?method1(10); // 斷點處
? }
?
? ?private static void method1(int x) {
? ? ? ?int y = x + 1;
? ? ? ?Object m = method2();
? ? ? ?System.out.println(m);
? }
?
? ?private static Object method2() {
? ? ? ?Object n = new Object();
? ? ? ?return n;
? }
}
在打斷點處,可以看到一個棧幀
執行到method1,可以看到新起了一個棧幀
當執行到method2時,可以看到又新起了一個棧幀
由于是棧,隨著的程序的運行,后面開啟的棧幀會先被銷毀,直至main棧幀被銷毀,此刻程序運行完成。
對應圖解:
內存釋放后
具體就是:
1.將編譯好的字節碼加載到jvm的方法區內存中 2.jvm啟動一個main的主線程,cpu核心就準備運行主線程的代碼了,給主線程分配自己的棧內存【args、局部變量、返回地址、所記錄】,每個線程的棧里面還有個程序計數器 程序計數器的作用:當cpu要執行哪行代碼了,就去這個里面去要 3.把主方法的里面代碼行放到程序計數器 4.主方法調用的是method1的方法,為method1分配棧內存,里面存儲這個方法里面局部變量,返回地址,這些變量是分配內存時,會把空間預留好 5.將method1的第一行讀到程序計數器讓cpu執行 6.methode1下一行調用method2()方法,創建他的棧內存 7.把Object n = new Object()這行代碼讀取到計數器,在隊中創建對象 8.method2()將返回地址給m,方法執行完就可以釋放掉method2()的棧內存 9.一層層方法結束后,依次釋放掉每個方法線程
現在來看看多線程下的棧與棧幀
public class TestFrames {
? ?public static void main(String[] args) {
? ? ? ?Thread t1 = new Thread(){
? ? ? ? ? ?@Override
? ? ? ? ? ?public void run() {
? ? ? ? ? ? ? ?method1(20);// 斷點處
? ? ? ? ? }
? ? ? };
? ? ? ?t1.setName("t1");
? ? ? ?t1.start();
? ? ? ?method1(10);// 斷點處
? }
?
? ?private static void method1(int x) {
? ? ? ?int y = x + 1;
? ? ? ?Object m = method2();
? ? ? ?System.out.println(m);
? }
?
? ?private static Object method2() {
? ? ? ?Object n = new Object();
? ? ? ?return n;
? }
}
?
在第一個斷點處
可以看到多個線程同時運行中,我們可以選擇具體的線程來查看運行狀況并且往下運行,具體的讀者可以自行實踐。
1.2 線程上下文切換(Thread Context Switch)
因為以下一些原因導致 cpu 不再執行當前的線程,轉而執行另一個線程的代碼(簡單來說就是從使用cpu到不使用cpu)
-
線程的 cpu 時間片用完
-
垃圾回收
-
有更高優先級的線程需要運行
-
線程自己調用了 sleep、yield、wait、join、park、synchronized、lock 等方法
當 Context Switch(上下文切換) 發生時,需要由操作系統保存當前線程的狀態,并恢復另一個線程的狀態,Java 中對應的概念就是程序計數器(Program Counter Register),它的作用是記住下一條 jvm 指令的執行地址,是線程私有的
-
狀態包括程序計數器、虛擬機棧中每個棧幀的信息,如局部變量、操作數棧、返回地址等
-