2015-01-07 26 views
1

我一直在試驗lambda,並遇到了一個我不明白的問題。以下代碼可以獨立運行,但以某種方式在多線程環境(我不控制)中運行此代碼會導致run()在一個實例中,並且doA()在不同的實例中,我可以將其視爲不同的實例ID在調試器中。從Lambda調用的方法無法訪問字段?

public class Main { 
    private String string; 

    public static void main(String[] args) { 
     Main main = new Main(); 
     Main main2 = new Main(); 
     main2.run(); 

    } 

    private interface Stepper { 
     boolean execute(); 
    } 

    Stepper[] steps = {() -> { return doA(); },() -> { return doB(); },() -> { return doC(); } }; 

    public boolean doA() { System.out.println(string); return true; } 
    public boolean doB() { System.out.println(string); string = "foo"; return true; } 
    public boolean doC() { System.out.println(string); return true; } 

    private void run() { 
     string = "changed"; 
     for (Stepper step : steps) { 
      if (step.execute() == false) { 
       return; 
      } 
     } 
    } 
} 

真實的類實現Runnable的專有後代,這裏沒有顯示。我將steps的初始化移動到run()方法中,並開始正常工作。 run()方法以外的陣列如何導致這種情況?

+4

你真的需要發佈一個可重複的例子。 –

+0

什麼字段?請向我們展示一個[最小但完整的示例](http://stackoverflow.com/help/mcve)。 – Radiodef

+0

閱讀更多內容後,我猜測這個問題與方法引用有關。早些時候,我的IDE建議我用方法引用替換lambda表達式,所以也許數組是用一些任意的'instance :: doA'實例化的? –

回答

1

如果您改變Stepper界面,你可以做這樣的事情:

public class Main { 
    private String string; 

    public static void main(String[] args) { 
     Main main = new Main(); 
     Main main2 = new Main(); 
     main2.run(); 

    } 

    private interface Stepper extends Function<Main, Boolean> { 
     default boolean execute(Main main){ 
      return this.apply(main); 
     } 
    } 

    Stepper[] steps = {Main::doA, Main::doB, Main::doC}; 

    public boolean doA() { System.out.println(string); return true; } 
    public boolean doB() { System.out.println(string); string = "foo"; return true; } 
    public boolean doC() { System.out.println(string); return true; } 

    private void run() { 
     string = "changed"; 
     for (Stepper step : steps) { 
      if (step.execute(this) == false) { 
       return; 
      } 
     } 
    } 
} 

這工作,因爲該方法引用是靜態的,但傳遞this的執行功能確保當前實例總是一個將會採取行動。

如果你有一個多線程環境,沒有任何東西阻止另一個線程同時運行並與這個實例的輸出交織。我建議你將你的輸出收集到一個StringBuilder並作爲一個操作打印出來,以確保輸出不交織。如果訂單不重要,那麼就足夠公平,但它確實會使調試更容易知道每組輸出來自同一個線程。你甚至可以考慮把線程ID附加到你的輸出行。