繁体   English   中英

在线程1中调用的构造方法,仅在线程2中访问的字段-是否需要volatile?

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

我有一个由主线程实例化的类。 然后,此类产生第二个线程,即处理线程。 处理线程调用类的某些方法(处理方法),这些方法访问/更改字段。 那些方法和字段只能由处理线程访问。 但是,初始化它们的构造函数在主线程上运行。

该类扩展了通用的“协议”类,该类包含输入处理线程,该输入处理线程调用用于处理接收到的消息的函数。 最初,我是在通用类的构造函数中自动启动处理线程的,结果发现这是一个非常愚蠢的主意:

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

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

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以独占方式访问该字段。 正确? 如果是这样,那么...

不需要挥发性/原子性。

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

17.4.2。 动作

线程间操作是由一个线程执行的操作,可以被另一个线程检测或直接影响该操作。 程序可以执行几种类型的线程间操作:

[...]

启动线程或检测到线程已终止的操作(第17.4.4节)。

-

17.4.4。 同步顺序

每个执行都有一个同步顺序。 同步顺序是执行中所有同步动作的总顺序。 对于每个线程t,t中的同步动作(第17.4.2节)的同步顺序与t的程序顺序(第17.4.3节)一致。

同步动作在动作上引发了同步关系,定义如下:

[...]

启动线程的动作与它启动的线程中的第一个动作同步。

-

17.4.5。 发生在订单之前

可以通过事前发生关系来排序两个动作。 如果一个动作发生在另一个动作之前,则第一个动作对第二个动作可见,并在第二个动作之前排序。

[...]

在启动线程中的任何操作之前,都会在线程上调用start()。

volatile意味着可以通过不同的线程修改特定字段。 如果构造函数被标记为synchronized ,则不需要它,否则它是必需的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM