0

我想確認這個我的理解 -多線程:線程獲取方法的自己的副本,但參數是共享

public class Main { 

    private static int j = 0; 
    private int k = 0; 

    public static void main(String[] args) { 
     Main obj = new Main(); 
     obj.doProcess(); 
    } 

    public void doProcess() { 
     ExecutorService service = Executors.newFixedThreadPool(10); 
     for (int i = 0; i < 4; i++) { 
      service.submit(new SingleProcessor()); 
     } 
    } 

    public static void myStaticMethod() { 
     System.out.println("my static method"); 
     int i = 0; 
     i++; 
     j++; 
     System.out.println("i " + i); 
     System.out.println("j " + j); 
    } 

    public void myInstanceMethod() { 
     System.out.println("my instance method"); 
     int i = 0; 
     i++; 
     k++; 
     System.out.println("k " + k); 
    } 

    private class SingleProcessor implements Runnable { 

     @Override 
     public void run() { 
      System.out.println("single run starts" + Thread.currentThread().getName()); 
      myStaticMethod(); 
      myInstanceMethod(); 
     } 

    } 
} 

當一個線程運行時,它得到它的方法自己的副本,無論是靜態的或實例方法 - 在這些方法中創建的任何變量都是本地的,並且是特定於該線程的。這就像這個方法的多個'實例'被同時執行一樣,任何在裏面創建的變量都不會被共享(它是本地的)。

但是參數(靜態或實例)由線程共享。

所以在上面的例子 - 我是本地和特定於線程。 j是共享的。 k是共享的。

輸出 -

single run startspool-1-thread-1 
single run startspool-1-thread-2 
my static method 
single run startspool-1-thread-3 
my static method 
i 1 
i 1 
j 2 
j 2 
my instance method 
my instance method 
k 1 
k 2 
my static method 
i 1 
j 3 
my instance method 
k 3 
single run startspool-1-thread-4 
my static method 
i 1 
j 4 
my instance method 
k 4 

我的理解是100%正確的?任何人都想用更好的話來表達它?

謝謝。

回答

1

如果我遵循了正確的 - 那應該是正確的。

它對堆棧和堆的基本理解。任何在棧上聲明的變量(在函數中聲明的變量)只能在本地使用。在堆中聲明的變量可以是全局訪問的,也可以是其範圍內的任何函數。

所以,如果你有一個線程處理一個函數,該函數內的所有變量只能在該函數內訪問。但是如果你有一個全局變量,並且兩個線程都可以訪問這個變量。問題在於,如果他們寫入該變量,則必須確保它們不會互相覆蓋。

解決方法是在一個線程讀取/寫入時鎖定堆變量,然後在完成時解鎖。

0

如果你運行下面的代碼,你認爲會發生什麼?您可能會看到類似的輸出,但順序不同。 i是一個靜態變量,處於靜態範圍,j在方法範圍內,k是一個實例變量Main。與線程相同的東西。範圍與線程一樣工作。您應該閱讀有關內部類的內容。

public class Main { 

    private static int j = 0; 
    private int k = 0; 

    public static void main(String[] args) { 
     Main obj = new Main(); 
     obj.doProcess(); 
    } 

    public void doProcess() { 
     for (int i = 0; i < 4; i++) { 
      SingleProcessor sp = new SingleProcessor(); 
      sp.run(); 
     } 
    } 

    public static void myStaticMethod() { 
     System.out.println("my static method"); 
     int i = 0; 
     i++; 
     j++; 
     System.out.println("i " + i); 
     System.out.println("j " + j); 
    } 

    public void myInstanceMethod() { 
     System.out.println("my instance method"); 
     int i = 0; 
     i++; 
     k++; 
     System.out.println("k " + k); 
    } 

    private class SingleProcessor implements Runnable { 

     @Override 
     public void run() { 
      System.out.println("single run starts" + Thread.currentThread().getName()); 
      myStaticMethod(); 
      myInstanceMethod(); 
     } 

    } 
} 
-1

你是對的。

一個靜態變量可以在應用程序中被解釋爲一個單例,不管你有多少個線程,它們總是會引用相同的變量。

至於你的實例變量「K」,它只能是因爲你的嵌套類不是靜態的,這意味着你需要主要實例能夠實例化單處理器。你的實例SingleProcessor將在引擎蓋下有一個隱藏的變量引用你主要的實例。

public class SingleProcessor implements Runnable { 

    private Main main; 

    public SingleProcessor(Main main){ 
     this.main = main; 
    } 

    @Override 
    public void run() { 
     System.out.println("single run starts" + Thread.currentThread().getName()); 
     Main.myStaticMethod(); 
     this.main.myInstanceMethod(); 
    } 

} 

並給所有的單處理器相同主要實例:彷彿you'ld寫這這是相同的。


所有這是危險的,一個線程可以獲取變量同時另一個線程修改它。有很多關於線程安全的高級主題。對於簡單類型,您可以使用原子變量,se Concurrency package JavaDoc

+0

我不認爲比較一個靜態變量與單例是一個好主意。靜態變量用於單例的事實是一種做法。如果不是最終的,則可以更改靜態變量。另外,我不認爲答案是關鍵的(例如討論stack vs heap) – 2014-09-02 23:26:54

+0

也許沒錯,我不應該使用Singleton這個詞。我試圖做一個'metaphore',以表明在內存中,任何線程都會使用相同的引用。它可能會令人困惑。 – JSlain 2014-09-03 01:09:15

+0

那是不正確的。一個靜態變量不會被java的內存模型區別對待。線程將得到一個副本,也沒有同步能見度的最新更新不保證 – eldjon 2014-09-03 08:55:23