2014-04-04 27 views
2

我想知道是否允許線程訪問類 的實例,以便您可以對該類的某些成員/變量執行操作。這可以處理線程和阻塞隊列嗎?

例如,我有一個主線程和一個線程。 我給第二個線程訪問主類 的實例,所以我可以在x上執行一個操作。

但是,如果在將來某個時候我決定在主線程中執行x 操作?或者只是簡單閱讀x。如果另一個線程和主線程想要同時讀取x,該怎麼辦?

這是否可以通過我在我的代碼中構造的方式來完成嗎?

package test; 

import java.lang.Thread; 
import java.util.List; 
import java.util.concurrent.BlockingQueue; 
import java.util.concurrent.LinkedBlockingQueue; 

class AThread extends Thread { 

    Test test; 

    AThread(Test test) { 
     this.test = test; 
    } 
    BlockingQueue<String> queue = new LinkedBlockingQueue<String>(); 

    public void run() { 
     String msg; 
     while ((msg = queue.poll()) != null) { 
      // Process the message 
      //System.out.println(msg); //should print "hello" 
      if (msg.equals("up")) { 
       test.setX(test.getX()+1); 
       System.out.println(test.getX()); 
      } 
     } 
    } 
} 

public class Test { 
    AThread aThread; 
    private int x = 5; 

    void setX(int x){ 
     this.x = x; 
    } 

    int getX(){ 
     return x; 
    } 

    Test() throws InterruptedException{ 
     System.out.println("MainThread"); 
     aThread = new AThread(this); 
     aThread.start(); 
     while (true) { 
      aThread.queue.put("up"); 

     } 
    } 

    public static void main(String[] args) throws InterruptedException { 
     new Test(); 
    } 
} 

而且不只是成員「X」,也有可能是在課堂上「測試」,我會希望能夠在這樣的執行操作如讀/寫更多的成員。

這是一個好的結構嗎?如果不是,應該修正什麼?

+0

對不起。 –

+0

好,很好。現在,* test.setX(test.getX()+ 1)怎麼做;'與[blocking]隊列有關,特別是當'test'甚至從未被這樣的隊列使用過?這仍然是問題的關鍵,還是關於使用[阻塞]隊列是否改變線程工作原理的問題? – user2864740

+0

我感覺這個問題屬於[Code Review](http://codereview.stackexchange.com/)。 –

回答

1

但是,如果在將來的某個時候我決定在主線程中對x進行操作呢?或者只是簡單閱讀x。如果另一個線程和主線程想要同時讀取x,會怎麼樣?

任何時候在兩個線程之間共享信息時,都需要提供內存同步。在這種情況下,如果您製作int xvolatile int x,那麼您的代碼應該正常工作。您應該閱讀Java tutorial on the subject。但是,如果線程正在執行更復雜的操作,與設置或獲取x相反,則可能需要使方法爲​​或以其他方式提供互斥鎖,以確保2個線程不會重疊不當。

例如,如果您需要增加x的值,則volatile將無濟於事,因爲增量實際上是3個操作:get,increment和set。您可以使用​​鎖來保護++,或者您應該考慮使用以線程安全方式處理incrementAndGet()方法的AtomicInteger

@ Segey的回答給出了關於其他代碼的一些很好的反饋。我將添加一條有關此代碼的評論:

while (true) { 
     aThread.queue.put("up"); 
    } 

你幾乎從不想這樣旋轉。如果你想做這樣的事情,那麼我會添加一些Thread.sleep(10)或者某些東西來減慢隊列中的添加或者使隊列大小有限。很可能您將耗盡內存,並像這樣創建隊列元素。

+1

和/或使用「AtomicInteger」對象。 – captainroxors

+0

但是,其他非整數對象如字符串呢? 另外,根據該示例,哪種方法需要同步? –

+0

你可以使'String'成爲'volatile'以及@JoeBid。你應該閱讀java教程:http://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html – Gray

2

你的代碼有幾個問題。

考慮這條線:

aThread = new AThread(this); 

它始終是一個壞主意,在構造函數中的某個地方傳this。這與線程無關......但。原因在於'somewhere'可能會調用this上的方法,並且該方法可能在其構造函數尚未調用的子類中被覆蓋,並且可能最終導致災難,因爲該覆蓋可能會使用某些子類字段尚未初始化。

現在,當線程進入圖片時,情況會變得更糟。一個線程保證能夠正確訪問在線程啓動之前創建的類實例。但在你的情況下,它還沒有創建,因爲構造函數尚未完成!而且它不會在任何地方很快完成,因爲下面的無限循環:

while (true) { 
     aThread.queue.put("up"); 

    } 

所以,你有一個對象創建並行運行的線程的啓動。 Java不保證線程在這種情況下會看到已初始化的類(即使沒有循環)。

這也是爲什麼在構造函數中啓動線程被認爲是壞主意的原因之一。有些IDE甚至會在這種情況下發出警告。請注意,在構造函數中運行無限循環也可能是一個糟糕的主意。

如果你移動你的代碼放到一個run()一種方法,做new Test().run()main(),那麼你的代碼看起來很好,但你是對的擔心

然而,如果在某些時候在什麼未來我決定在主線程中對x執行操作 ?

最好的辦法是在主線程它被傳遞給線程後權的客體忘記:固定錯字

public static void main(String[] args) throws InterruptedException { 
    AThread aThread = new AThread(new Test()); 
    aThread.start(); 
    while (true) { 
     aThread.queue.put("up"); 
    } 
} 
+0

除了最後一個,你所有的答案都很好。你的最後一個例子不會編譯,因爲不再有_aThread_變量。你故意「忘記」它。 –

+0

@james:哎呀!我故意忘了'new Test()'對象,但不是線程!我發誓,那是個意外。編輯。 –