以下のようなものに出会ったのはこれが初めてです。
データ構造(上位クラスのインスタンス変数)を共有する複数のスレッド(Runnableを実装する内部クラス)。
動作中:Eclipseプロジェクトのbinフォルダーからクラスを取得し、Unixマシンで実行しました。
動作しない:Unixマシンでsrcを直接コンパイルし、それらのクラスファイルを使用しました。コードはコンパイルされ、エラーや警告なしで実行されますが、1つのスレッドが共有リソースに正しくアクセスできません。
問題:1つのスレッドが上記の一般的なDSに要素を追加します。2番目のスレッドは次のことを行います...
while(true){ if(myArrayList.size() > 0){ //do stuff }
}
ログは、サイズがスレッド1で更新されたことを示しています。
いくつかの神秘的な理由により、ワークフローはif()...
Eclipseのbinフォルダーからクラスファイルを直接貼り付けると、まったく同じコードが完全に実行されます。
明らかなことを見逃してしまったことをお詫び申し上げます。
コード:
ArrayList<CSRequest> newCSRequests = new ArrayList<CSRequest>();
//スレッド1
private class ListeningSocketThread implements Runnable {
ServerSocket listeningSocket;
public void run() {
try {
LogUtil.log("Initiating...");
init(); // creates socket
processIncomongMessages();
listeningSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private void processIncomongMessages() throws IOException {
while (true) {
try {
processMessage(listeningSocket.accept());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
private void processMessage(Socket s) throws IOException, ClassNotFoundException {
// read message
ObjectInputStream ois = new ObjectInputStream(s.getInputStream());
Object message = ois.readObject();
LogUtil.log("adding...: before size: " + newCSRequests.size());
synchronized (newCSRequests) {
newCSRequests.add((CSRequest) message);
}
LogUtil.log("adding...: after size: " + newCSRequests.size()); // YES, THE SIZE IS UPDATED TO > 0
//closing....
}
........
}
//Thread 2
private class CSRequestResponder implements Runnable {
public void run() {
LogUtil.log("Initiating..."); // REACHES..
while (true) {
// LogUtil.log("inside while..."); // IF NOT COMMENTED, FLOODS THE CONSOLE WITH THIS MSG...
if (newCSRequests.size() > 0) { // DOES NOT PASS
LogUtil.log("inside if size > 0..."); // NEVER REACHES....
try {
handleNewCSRequests();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
....
}
アップデート
解決策は、スレッド2でサイズを確認する前に、synchronized(myArrayList)を追加することでした。