Search code examples
javaconcurrencyconstructorvolatile

Constructor called in Thread 1, fields accessed exclusively in Thread 2 - volatile needed?


I have a class that is being instantiated by the main thread. This class then spawns a second thread, the processing thread. The processing threads calls certain methods (handling methods) of the class, which access/change fields. Those methods and fields are never accessed by anything else than the processing thread. However, the constructor which initializes them runs on the main thread.

The class extends a generic "protocol" class, which contains the input processing thread, which calls the function for handling received messages. Originally, I was automatically starting the processing thread in the constructor of the generic class, which turned out to be a terribly stupid idea:

  1. The subclass called the super constructor
  2. The super constructor started the thread
  3. The thread immediately called the message handling method with an empty message (to make it send the first message in the protocol). The method set a "sent message counter".
  4. On the main thread, the super constructor returned, and the subclass initialized the set message counter, resetting it to zero.

I have now changed it by moving the starting of the processing thread to another method and calling it at the end of the constructor of the subclass:

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

I assume that when I call startProcessingThreads(), the field is guaranteed to be initialized. After startProcessingThread() is called, the field will only be accessed from that thread. Can I do this? Do I need to mark the field volatile, since it gets initialized on the main thread but read on the processing thread?

I think I got it right this time, but after hours of debugging the above-mentioned issue, I'd rather ask...

As requested, here is slightly more detailed (still simplified) code. Note that the code above is much more simplified and thus may not exactly match the code below. The field that was acting up was 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++;
    }

}

Solution

  • The field is initialized in thread 1; then thread 2 is started; then thread 2 exclusively accesses the field. Correct? If so, then...

    Volatile/atomic is not needed.

    Based on the JLS, an action performed in some thread A before thread B was started is visible to thread B. This is stated in a few different ways:

    17.4.2. Actions

    An inter-thread action is an action performed by one thread that can be detected or directly influenced by another thread. There are several kinds of inter-thread action that a program may perform:

    [...]

    Actions that start a thread or detect that a thread has terminated (§17.4.4).

    --

    17.4.4. Synchronization Order

    Every execution has a synchronization order. A synchronization order is a total order over all of the synchronization actions of an execution. For each thread t, the synchronization order of the synchronization actions (§17.4.2) in t is consistent with the program order (§17.4.3) of t.

    Synchronization actions induce the synchronized-with relation on actions, defined as follows:

    [...]

    An action that starts a thread synchronizes-with the first action in the thread it starts.

    --

    17.4.5. Happens-before Order

    Two actions can be ordered by a happens-before relationship. If one action happens-before another, then the first is visible to and ordered before the second.

    [...]

    A call to start() on a thread happens-before any actions in the started thread.