2014-09-03 106 views
2

在我的Java應用程序中,我有一個JTabbedPane在主JFrame與許多JPanel選項卡。該選項卡中我有一個函數從SwingUtilities.InvokeLater調用獲取返回值?

public void newStatus(final String arg) 
{ 
    Runnable sendMsg = new Runnable() 
    { 
     @Override 
     public void run() 
     { 
      mainView.newStatusLine(arg); 
     } 
    }; 
    SwingUtilities.invokeLater(sendMsg); 
} 

這個函數調用主JFrame mainView函數寫一些文字到JTextPane。我的問題是,這不允許我從主JFrame獲得返回值。我想這樣做

public InfoObj getInfo() 
{ 
    Runnable sendMsg = new Runnable() 
    { 
     @Override 
     public void run() 
     { 
      return mainView.getInfo(); 
     } 
    }; 
    SwingUtilities.invokeLater(sendMsg); 
} 

但我不知道如何使這項工作。我嘗試過並遵循IDE的消息,看是否可以使用它,但我無法覆蓋Runnable.Run以返回某些內容。

是否有任何機制可以做到這一點?

編輯:對於HoverCraftFullOfEels,整體問題是JPanels之間,主JFrameJPanel之間的談話。在某個時間點,JPanel想告訴主要的JFrame做些什麼,或者它可能想從中獲取一些數據。但是從我所知道的一切來看,我不能僅僅將this的引用傳遞給JFrameJPanel,並用它來調用公共函數或讀取某個公共字段。

有時候我想通過產生的線程在EDT上做這件事。一些JPanel產卵線程,我想通過JPanel的主要參考JFrame,所以它可以調用它的函數告訴用戶有關線程正在做什麼。

+0

這有可能是XY問題的味道。更好地告訴我們你想要解決的整體問題,而不是你想如何用代碼解決問題。另外爲什麼甚至在這裏將一個Runnable排隊在這裏?我認爲這個代碼已經被EDT調用過了,不是嗎? – 2014-09-03 02:47:07

+0

**以後**的哪部分你不明白?該代碼尚未運行。 – SLaks 2014-09-03 02:49:21

+0

通常返回您需要的東西['Callable'](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Callable.html)。 – 2014-09-03 02:54:19

回答

5

看起來您的應用程序數據全部綁定在您的用戶界面對象中。如果你所做的一切都可以在EDT上完成,那麼你應該沒問題。您可以在需要彼此信息的對象中直接調用。由於所有這些都在EDT上,因此它實際上是單線程的,並且沒有競爭條件或死鎖。但是,這可以將UI代碼的各個部分彼此耦合得太緊密。在這種情況下,您可以按照評論者的建議使用某種觀察者或聽衆模式。這在AWT/Swing等單線程,事件驅動的環境中工作正常。

但是你說你可能想從EDT以外的某個線程做到這一點。這改變了一切。 (另外,這也是導致人們考慮將你的UI對象中的所有應用程序數據綁定爲反模式的原因之一,這使得從外部獲取這些數據變得非常困難UI子系統。)

可能添加觀察者模式,但是有限制。 UI擁有數據,因此只有EDT可以將數據和事件更改爲觀察者。觀察員名單需要以線程安全的方式進行維護。據推測,觀察員會想要更新響應事件的數據結構。這些數據結構必須是線程安全的,因爲它們可以從EDT和其他線程訪問。這種方法可行,但你必須仔細考慮哪個線程在調用哪些方法,哪些數據結構必須是線程安全的。

假設你不想走這條路線,讓我們回到你原來的問題,這是關於如何從invokeLater返回的東西。這樣做可以讓您在UI中保留數據,同時允許其他線程在需要時從UI中獲取數據。它可能做到這一點,有點間接。但它確實有風險。

這是代碼。這可以從「其他」(非EDT)線程中調用。

InfoObj getInfo() { 
    RunnableFuture<InfoObj> rf = new FutureTask<>(() -> getInfoObjOnEDT()); 
    SwingUtilities.invokeLater(rf); 
    try { 
     return rf.get(); 
    } catch (InterruptedException|ExecutionException ex) { 
     ex.printStackTrace(); // really, you can do better than this 
    } 
} 

的這裏訣竅是,RunnableFuture一個RunnableFuture的接口。這很有用,因爲invokeLater只需要一個Runnable,但Future可以返回一個值。更具體地說,Future可以捕獲一個線程中返回的值並存儲它,直到另一個線程想要獲取它。 FutureTask是一個方便的現成的RunnableFuture實現,其參數Callable是一個可以返回值的函數。

基本上我們創建一個FutureTask,並且在EDT上運行一段代碼(Callable,這個例子中的lambda表達式),它將運行在EDT上。這將收集我們想要的信息並將其返回。然後我們使用invokeLater將其發佈到EDT。當另一個線程想要數據時,它可以立即調用get(),或者可以掛起Future,稍後再撥get()。有一個小的不便之處,Future.get()會拋出一些您必須處理的檢查異常。

這是如何工作的?如果EDT首先運行Callable,則返回值將存儲在Future中,直到另一個線程調用get()爲止。如果其他線程首先調用get(),則該線程將被阻塞,直到該值變爲可用。

而且還有問題。精明的讀者會認識到這與invokeAndWait()具有相同的風險,即如果您不小心,可能會導致系統死鎖。這可能是因爲調用get()的線程可能會阻止等待EDT來處理由invokeLater發佈的事件。如果由於某種原因EDT被阻塞 - 可能正在等待其他線程持有的東西 - 結果就是死鎖。要避免這種情況,在調用可能阻止等待EDT的東西時要格外小心。一個很好的通用規則是在調用其中一種阻塞方法時不要鎖定任何鎖。

有關如何使用invokeAndWaitinvokeLater(FutureTask)來解決問題的示例,請參閱this questionmy answer

如果您的其他線程完全與UI數據結構分離,則此技術應該非常有效。

+0

我認爲我的問題的答案在這裏。但它不適用於某些真實世界,因此我無法對其進行適當的審查。我在這裏問過類似的問題http://stackoverflow.com/questions/25649950/passing-data-between-swing-components-and-worker-threads如果你會爲我檢討它? – KDecker 2014-09-03 17:05:25