我有一个由主线程实例化的类。然后这个类产生第二个线程,处理线程。处理线程调用类的某些方法(处理方法),这些方法访问/更改字段。除了处理线程之外,其他任何东西都不会访问这些方法和字段。但是,初始化它们的构造函数在主线程上运行。
该类扩展了一个通用的“协议”类,其中包含输入处理线程,该线程调用用于处理接收到的消息的函数。最初,我是在泛型类的构造函数中自动启动处理线程,结果证明这是一个非常愚蠢的想法:
- 子类称为超级构造函数
- 超级构造函数启动线程
- 线程立即用空消息调用消息处理方法(使其发送协议中的第一条消息)。该方法设置一个“发送消息计数器”。
- 在主线程上,超级构造函数返回,子类初始化设置的消息计数器,将其重置为零。
我现在通过将处理线程的开始移动到另一个方法并在子类的构造函数的末尾调用它来更改它:
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++;
}
}