2013-03-13 18 views
0

我有一個正在被主線程實例化的類。然後這個類產生第二個線程,即處理線程。處理線程調用該類的某些方法(處理方法),訪問/更改字段。這些方法和字段永遠不會被處理線程以外的其他任何東西訪問。但是,初始化它們的構造函數在主線程上運行。在Thread 2中調用的構造函數,在Thread 2中專門訪問的字段 - volatile需要嗎?

該類擴展了一個通用「協議」類,該類包含輸入處理線程,該線程調用處理收到的消息的函數。本來,我是自動啓動的泛型類的構造函數,它原來是一個非常愚蠢的想法處理線程:

  1. 子類稱爲超級構造
  2. 超級構造函數啓動線程
  3. 線程立即使用空消息調用消息處理方法(以使其在協議中發送第一條消息)。該方法設置了「發送消息計數器」。
  4. 在主線程上,超級構造函數返回,並且子類初始化設置消息計數器,將其重置爲零。

我現在已經被加工螺紋的起點移動到另一個方法,並在子類的構造函數的最後調用它改變了它:

public ProtocolSubclass() { 
    super(); 
    startProcessingThread(); 
} 

我認爲當我打電話startProcessingThreads( ),該字段保證被初始化。在調用startProcessingThread()後,只能從該線程訪問該字段。 我可以這樣做嗎?我是否需要將字段標記爲易失性的,因爲它在主線程上初始化,但在處理線程上讀取?

我想我猜中了這個時間,但調試上述問題小時後,我寧願問...

按照要求,這裏是略爲詳細(還是簡化的)代碼。請注意,上面的代碼更加簡化,因此可能不完全符合以下代碼。這是演戲了本場是currentMsg:

public abstract class ProtocolConnection { 
    public ProtocolConnection(/*stuff*/) { 
     /*stuff*/ 
     // DO NOT DO THIS HERE: startProcessingThreads(); 
    } 

    protected void startProcessingThreads() { 
     inputProcessingThread.start(); 
    } 

    private final Thread inputProcessingThread = new Thread() { 
     public void run() { 
      if (isInitiator) initiateConnection(); 
      while (!closed && !finished) { 
       ProtocolMessage msg = new ProtocolMessage(inputStream); 
       log("received", Integer.toString(msg.tag), Integer.toString(msg.length)); 
       ProtocolConnection.this.processMessage(msg); 
      } 
     } 
    }; 
} 


public class SimpleProtocolConnection extends ProtocolConnection { 
    private int currentMsg = 0; 

    public SimpleProtocolConnection(/*stuff*/) { 
     super(/*stuff*/); 
     startProcessingThreads(); 
    } 

    @Override 
    protected void processMessage(ProtocolMessage msg) { 
     if (msg.tag != LAST_MESSAGE) { 
      sendNext(); 
     } 
    } 

    @Override 
    protected void initiateConnection() { 
     sendNext(); 
    } 

    private void sendNext() { 
     addToSendingQueue(new ProtocolMessage(currentMsg, getData())); // very simplified 
     currentMsg++; 
    } 

} 
+0

你能提供更多的代碼來真正體現安裝嗎? – 2013-03-13 02:25:40

+0

如果在構造函數中設置的所有字段都是'final'(這是不太可能的),那麼你不必擔心。 – user949300 2013-03-13 02:40:47

+0

@MattBall:添加代碼。 – 2013-03-13 02:53:44

回答

2

該字段在線程1中初始化;然後啓動線程2;那麼線程2將獨佔訪問該字段。正確?如果是這樣,那麼...

揮發性/原子是不需要的。

基礎上JLS,線程B開始之前在某些線程A執行的操作是可見的線程B,這是在幾個不同的方式表示:

17.4.2。操作

線程間操作是由一個線程執行的操作,該操作可以檢測到或直接受另一個線程影響 。有 幾種線程間的行動,一個程序可以執行:

[...]

操作該啓動一個線程或檢測到一個線程終止 (§17.4.4)。

-

17.4.4。同步訂單

每次執行都有一個同步順序。同步訂單 是執行所有同步操作的總訂單。對於每個線程t,t中的同步操作(第17.4.2節)的同步順序與t的程序 順序(第17.4.3節)一致。

同步動作誘導同步-與 動作,限定的關係如下:

[...]

啓動一個線程的動作同步-與第一動作中 它的線程開始。

-

17.4.5。發生在訂單之前

兩個動作可以通過發生之前的關係進行排序。如果一個 動作發生 - 在另一個動作之前,則第一個可見並且在第二個之前排序並且 。

[...]

調用開始()上線之前發生在 任何行動開始線程。

+1

謝謝,那正是我想要的。要添加:[JLS的這部分](http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.5)解釋說,在初始化期間,超級構造函數首先,實例初始化器在此之後,其餘的構造器代碼最後。這意味着在超級構造函數中啓動線程可以給出有趣的結果,而在包含該字段的子類的構造函數代碼中啓動它應該沒問題。 – 2013-03-13 03:23:49

-1

volatile意味着某一特定領域將我通過不同的線程修改。如果構造函數被標記爲​​,則不需要,否則它是必需的。

+0

構造函數不能在Java中標記爲同步。 – 2013-03-13 02:40:31

+0

這個答案充其量是誤導。 'volatile'只是意味着所有線程都可以看到字段的更改。 http://docs.oracle.com/javase/tutorial/essential/concurrency/atomic.html'synchronized'永遠不會替代'volatile'。 – 2013-03-13 02:57:50

相關問題