java - 在线程 1 中调用构造函数,在线程 2 中独占访问字段 - 需要 volatile 吗?

标签 java concurrency constructor volatile

我有一个正在由主线程实例化的类。然后,该类生成第二个线程,即处理线程。处理线程调用类的某些方法(处理方法),这些方法访问/更改字段。除了处理线程之外,这些方法和字段永远不会被其他任何东西访问。但是,初始化它们的构造函数在主线程上运行。

该类扩展了一个通用“协议(protocol)”类,其中包含输入处理线程,该线程调用处理接收到的消息的函数。最初,我在泛型类的构造函数中自动启动处理线程,结果证明这是一个非常愚蠢的想法:

  1. 子类调用 super 构造函数
  2. super 构造函数启动了线程
  3. 线程立即使用空消息调用消息处理方法(以使其发送协议(protocol)中的第一条消息)。该方法设置了一个“发送消息计数器”。
  4. 在主线程上, super 构造函数返回,子类初始化设置的消息计数器,将其重置为零。

我现在通过将处理线程的启动移动到另一个方法并在子类构造函数的末尾调用它来更改它:

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

我假设当我调用startProcessingThreads()时,该字段保证被初始化。调用 startProcessingThread() 后,只能从该线程访问该字段。 我可以这样做吗?我是否需要将该字段标记为 volatile ,因为它在主线程上初始化但在处理线程上读取?

我想这次我是对的,但是经过几个小时的调试上述问题后,我宁愿问...

根据要求,这里是稍微更详细(仍然简化)的代码。请注意,上面的代码更加简化,因此可能与下面的代码不完全匹配。起作用的字段是 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++;
    }

}

最佳答案

该字段在线程1中初始化;然后启动线程2;然后线程 2 独占访问该字段。正确的?如果是这样,那么...

不需要 volatile /原子性。

基于JLS ,在线程 B 启动之前在某个线程 A 中执行的操作对于线程 B 是可见的。这可以用几种不同的方式来表示:

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.

关于java - 在线程 1 中调用构造函数,在线程 2 中独占访问字段 - 需要 volatile 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15375895/

相关文章:

java - 如何检查 Android 设备上安装的字体

Java future 和快速点积

c++ - 使用虚拟继承时初始化基类

java - 在 Struts 2 中使用 Ajax 和 Oracle 数据库自动完成文本框

java - TreeMap - 搜索时间复杂度

java - 模拟 void 方法返回空指针异常

node.js - 哪些函数可以在 Node.js 中同步工作?

multithreading - Clojurescript中如何实现Atom?

javascript - ImmutableJS 无法使用新的自定义对象实例

c++ - 使用*指针数组 C++ 设置构造函数和析构函数