0

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

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

  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++;
    }

}
4

2 回答 2

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。订单前发生

两个动作可以通过happens-before关系排序。如果一个动作发生在另一个动作之前,那么第一个动作对第二个动作可见并在第二个动作之前排序。

[...]

线程上的 start() 调用发生在已启动线程中的任何操作之前。

于 2013-03-13T03:15:05.147 回答
-1

volatile意味着一个特定的字段将被不同的线程修改。如果构造函数被标记synchronized,则不需要,否则需要。

于 2013-03-13T02:27:52.333 回答