為什么多線程環境下兩個不同的線程可以訪問主線程中的局部變量point?

為什么多線程環境下兩個不同的線程可以訪問主線程中的局部變量point?

Java線程局部變量訪問機制詳解

java多線程編程中,局部變量的訪問機制常常引發疑問。本文將深入探討多線程環境下,不同線程訪問主線程局部變量的原理,并澄清一些常見的誤解。

文中提到的示例圖展示了主線程和兩個子線程,子線程能夠訪問主線程中的局部變量point。 添加代碼后,子線程無法再訪問point,這與內部類“effectively final”的限制有關。

文中開發者推測,子線程能夠訪問point,是因為Runnable的兩個實現類分別創建了point的實例變量。 這種解釋在局部內部類中可能成立,但在一般多線程環境下并不適用。

實際上,子線程能夠訪問主線程局部變量point的根本原因并非創建新的實例變量,而是編譯器優化線程的運作方式。

關鍵在于“棧封閉”(Stack Confinement): java編譯器會對代碼進行優化,如果一個局部變量只在單個方法內被訪問,且沒有被修改(或被聲明為final),那么編譯器可能會將該變量直接嵌入到線程的棧幀中。 這意味著每個線程擁有該局部變量的一個獨立副本。

并非共享,而是副本: 子線程訪問的并非主線程棧幀中的point,而是其自身棧幀中編譯器生成的副本。因此,即使子線程修改了其副本的值,也不會影響主線程中的原始point。

示例說明:

以下示例代碼更清晰地闡述了這一點:

public static void main(String[] args) {     User user = new User("defaultName");     Runnable runnable = () -> {         // 這里訪問的是user的副本,修改副本不影響原值         System.out.println("Thread 1: " + user.getName()); // 輸出 defaultName         user.setName("name1");         System.out.println("Thread 1: " + user.getName()); // 輸出 name1     };     Thread thread1 = new Thread(runnable);     thread1.start();     System.out.println("Main Thread: " + user.getName()); // 輸出 defaultName }  static class User {     private String name;      public User(String name) {         this.name = name;     }      public String getName() {         return name;     }      public void setName(String name) {         this.name = name;     } }

在這個例子中,user在主線程和子線程中都被訪問,但子線程修改的只是其本地副本。主線程中的user保持不變。 如果user被聲明為final,則子線程只能讀取,不能修改。

總結: 多線程環境下,對局部變量的訪問是通過棧封閉機制實現的,每個線程擁有局部變量的獨立副本。這保證了線程安全,避免了數據競爭。 文中提到的情況,子線程訪問的是局部變量的副本,而不是共享同一個變量。 開發者添加代碼后子線程無法訪問,是因為修改了變量的訪問范圍,不再滿足編譯器優化的條件。

? 版權聲明
THE END
喜歡就支持一下吧
點贊6 分享