4

以下のようなものに出会ったのはこれが初めてです。

  • データ構造(上位クラスのインスタンス変数)を共有する複数のスレッド(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)を追加することでした。

4

4 に答える 4

5

マルチスレッド環境で共有構造にアクセスするには、暗黙的または明示的なロックを使用して、スレッド間の安全な公開とアクセスを確保する必要があります。上記のコードを使用すると、次のようになります。

while(true){
    synchronized (myArrayList) {
        if(myArrayList.size() > 0){
            //do stuff
        }
    }
    //sleep(...) // outside the lock!
}

注:このパターンは生産者/消費者によく似ており、キューを使用して実装する方が適切です。LinkedBlockingQueueはそのための優れたオプションであり、組み込みの同時実行制御機能を提供します。これは、スレッド間でデータを安全に公開するための優れた構造です。同時データ構造を使用すると、同期されたブロックを取り除くことができます。

Queue queue = new LinkedBlockingQueue(...)
...
while(true){
        Data data = queue.take(); // this will wait until there's data in the queue
        doStuff(data);
}
于 2012-10-26T06:59:20.053 に答える
1

shared variable特定の内部parallel region(複数のスレッドが並行して実行されている領域)を変更するたびに、を確認する必要がありますmutual exclusionmutual exclusionJavaでは、synchronizedまたはを使用して保証できlocksます。通常、より細かい同期が必要な場合はロックを使用します。

プログラムが特定の共有変数のパフォーマンス読み取りのみを行う場合、この変数へのアクセスを同期/ロックする必要はありません。

あなたはこの主題に不慣れなので、私はあなたにこのチュートリアルをお勧めします

于 2012-10-26T10:05:50.383 に答える
0

私がこれを正しく理解した場合..同じ共有データ構造で動作するスレッドが少なくとも2つあります。あなたが言及した配列..配列のサイズが0より大きい場合、1つのスレッドが配列に値を追加し、2番目のスレッドが「処理を実行」します。スレッドスケジューラが2番目のスレッドを実行した可能性があります(コレクションが>であるかどうかをチェックします)。 0)、最初のスレッドが実行されて値を追加する機会が得られる前。binからクラスを実行したり、クラスを再コンパイルしたりすることは何の関係もありません。binディレクトリからアプリケーションをもう一度実行すると、問題が再び発生する可能性があります。アプリを何回実行しましたか?一貫して再現されない場合がありますが、ある時点で再び問題が発生する可能性があります。

シリアル方式でデータ構造にアクセスし、一度に1つのスレッドのみがアレイにアクセスできるようにすることができます。それでも、最初のスレッドが実行されることを保証するものではなく、2番目のスレッドがサイズ>0かどうかをチェックします。

あなたが達成する必要があるものに応じて、それを達成するためのより良い/他の方法があるかもしれません。必ずしも配列を使用してスレッドを調整する必要はありません。

于 2012-10-26T07:23:08.007 に答える
0

のリターンを確認してください

newCSRequests.add((CSRequest) message);

なんらかの理由で追加されなかった可能性があると思います。HashSetなどの場合は、複数のオブジェクトのハッシュコードが同じ値を返すことが原因である可能性があります。メッセージオブジェクトのequals実装とは何ですか?

あなたも使うことができます

List list = Collections.synchronizedList(new ArrayList(...));

配列リストが常に正しく同期されるようにします。

HTH

于 2012-10-26T07:07:36.930 に答える