メインスレッドによってインスタンス化されているクラスがあります。次に、このクラスは2番目のスレッドである処理スレッドを生成します。処理スレッドは、フィールドにアクセス/変更するクラスの特定のメソッド(処理メソッド)を呼び出します。これらのメソッドとフィールドは、処理スレッド以外からアクセスされることはありません。ただし、それらを初期化するコンストラクターはメインスレッドで実行されます。
このクラスは、受信したメッセージを処理するための関数を呼び出す入力処理スレッドを含む汎用の「プロトコル」クラスを拡張します。もともと、私はジェネリッククラスのコンストラクターで処理スレッドを自動的に開始していましたが、これはひどくばかげた考えであることが判明しました。
- スーパーコンストラクターと呼ばれるサブクラス
- スーパーコンストラクターがスレッドを開始しました
- スレッドは、空のメッセージを使用してメッセージ処理メソッドをすぐに呼び出しました(プロトコルの最初のメッセージを送信するため)。このメソッドは「送信メッセージカウンター」を設定します。
- メインスレッドで、スーパーコンストラクターが返され、サブクラスが設定されたメッセージカウンターを初期化して、ゼロにリセットしました。
処理スレッドの開始を別のメソッドに移動し、サブクラスのコンストラクターの最後で呼び出すことで、これを変更しました。
public ProtocolSubclass() {
super();
startProcessingThread();
}
startProcessingThreads()を呼び出すと、フィールドが初期化されることが保証されていると思います。startProcessingThread()が呼び出された後、フィールドにはそのスレッドからのみアクセスされます。これはできますか?フィールドはメインスレッドで初期化されますが、処理スレッドで読み取られるため、フィールドを揮発性としてマークする必要がありますか?
今回は正解だったと思いますが、上記の問題を何時間もデバッグした後、むしろ質問したいと思います...
要求に応じて、ここにもう少し詳細な(まだ簡略化された)コードがあります。上記のコードははるかに単純化されているため、以下のコードと正確に一致しない場合があることに注意してください。動作していたフィールドは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++;
}
}