8

更新: 完全な回答については、この質問の下部を参照してください。

メイン スレッドとセカンダリ スレッドが交互に操作を実行できるように、セカンダリ スレッドを実行したい (いいえ、メイン スレッドですべての操作を実行する必要はありません。単体テスト用です)。

私は 2 つの異なる解決策を思いつきました。どちらが最適かはわかりません。最初の解決策について質問があります。

エクスチェンジャーの使用

Exchangerを使用して何かを思いつきました(ただし、1 つのオブジェクトだけを交換したくはありません)。

@Test
public void launchMyTest() {
    /**
     * An anonymous class to set some variables from a different thread
     */
    class ThreadTest extends Thread {
        //declare some various attributes that will be set
        //NOT DECLARED VOLATILE
        ...

        public final Exchanger<Integer> exchanger = new Exchanger<Integer>();

        @Override
        public void run() {
            try {
                //start of the synchronization 
                int turn = 1;
                while (turn != 2) {
                    turn = this.exchanger.exchange(turn);
                }

                //do some work and set my various variables
                ...

                //main thread's turn
                turn = 1;
                this.exchanger.exchange(turn);
                //wait for this thread's turn
                while (turn != 2) {
                    turn = this.exchanger.exchange(turn);
                }

                //redo some other work and reset the various variables
                ...

                //main thread's turn
                turn = 1;
                this.exchanger.exchange(turn);

            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } 
        }
    }


    try {
        //some work in the main thread
        ....

        //launch the job in the second thread
        ThreadTest test = new ThreadTest();
        test.start();
        //start of the synchronization
        int turn = 2;
        test.exchanger.exchange(turn);
        //wait for this thread's turn
        while (turn != 1) {
            turn = test.exchanger.exchange(turn);
        }

        //run some tests using the various variables of the anonymous class
        ....

        //now, relaunch following operations in the second thread
        turn = 2;
        test.exchanger.exchange(turn);
        //wait for this thread's turn
        while (turn != 1) {
            turn = test.exchanger.exchange(turn);
        }

        //do some other tests using the various variables of the anonymous class
        //...

    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
}

質問:

  • exchangeメソッドがメモリ同期を実行するのと同じくらい正しいLockですか?

使用条件

Conditionを使用した別のソリューション:

@Test
public void launchMyTest() {
    /**
     * An anonymous class to set some variables from a different thread
     */
    class ThreadTest extends Thread {
        //declare some various attributes that will be set
        //NOT DECLARED VOLATILE
        ...

        public final Lock lock = new ReentrantLock();
        public final Condition oneAtATime = lock.newCondition();
        public int turn = 1;

        @Override
        public void run() {
            this.lock.lock();
            try {
                //do some work and set my various variables
                ...

                //main thread's turn
                this.turn = 1;
                this.oneAtATime.signal();

                //wait for this thread's turn
                while (this.turn != 2) {
                    this.oneAtATime.await();
                }

                //redo some other work and reset the various variables
                ...

                //main thread's turn
                this.turn = 1;
                this.oneAtATime.signal();

            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                this.lock.unlock();
            }
        }
    }


    ThreadTest test = new ThreadTest();
    test.lock.lock();
    try {
        //some work in the main thread
        ....

        //launch the job in the second thread
        test.turn = 2;
        test.start();
        //wait for this thread's turn
        while (test.turn != 1) {
            test.oneAtATime.await();
        }

        //run some tests using the various variables of the anonymous class
        ....

        //now, relaunch following operations in the second thread
        test.turn = 2;
        test.oneAtATime.signal();
        //wait for this thread's turn
        while (test.turn != 1) {
            test.oneAtATime.await();
        }

        //do some other tests using the various variables of the anonymous class
        //...

    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    } finally {
        test.lock.unlock();
    }
}

私にはもう少し複雑に思えます。

結論

最善の解決策は何だと思いますか? 私はそれを正しくやっていますか、それとも別の明らかな解決策を見逃していますか?

いくつかの操作を交互CountDownLatchに実行したいので、を使用しませんでした。また、をリセットすることはできません。そして、 がコードを単純化しているとは思いませんでした... (実際には、使用方法を完全には理解していませんでしたが、 orを使用するよりも単純に見えませんでした)CountDownLatchCyclicBarrierExchangerCondition

ありがとうございました。

アップデート

@Clément MATHIEUは、これを達成する方法のさまざまな例を提供しました。受け入れられた回答のコメントで、 https ://gist.github.com/cykl/5131021を参照してください。

CyclicBarrierを使用した例、 を使用した例、および 2 を使用Exchangerした最後の例の 3 つの例がありますSemaphore。彼が「より表現力豊かなのはセマフォベースのものだ」と言うのは正しいですが、私はExchanger単純化のために を使用することにしました。私の単体テストは次のようになりました。

@Test
public void launchMyTest() {
    /**
     * An anonymous class to set some variables from a different thread
     */
    class ThreadTest extends Thread {
        //declare some various attributes that will be set
        //NOT DECLARED VOLATILE
        ...
        public final Exchanger<Integer> exchanger = new Exchanger<Integer>();

        @Override
        public void run() {
            try {
                //do some work and set my various variables
                ...

                //main thread's turn
                this.exchanger.exchange(null);
                //wait for this thread's turn
                this.exchanger.exchange(null);

                //redo some other work and reset the various variables
                ...

                //main thread's turn
                this.exchanger.exchange(null);

            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } 
        }
    }


    try {
        //some work in the main thread
        ....

        //launch the job in the second thread
        ThreadTest test = new ThreadTest();
        test.start();
        //wait for this thread's turn
        test.exchanger.exchange(null);

        //run some tests using the various variables of the anonymous class
        ....

        //now, relaunch following operations in the second thread
        test.exchanger.exchange(null);
        //wait for this thread's turn
        test.exchanger.exchange(null);

        //do some other tests using the various variables of the anonymous class
        //...

    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
}
4

4 に答える 4

2

交換器は正しく見えます。http://www.youtube.com/watch?v=WTVooKLLVT8を見た後、変数は揮発性である必要があると思います、頭上はほとんどないと言います。

于 2013-03-10T20:31:30.770 に答える
1

ロックを使用するのと同じように、交換方法がメモリ同期を実行することは正しいですか?

あなたが正しいです。javadocは、発生前の関係があることを指定しています。

メモリの一貫性への影響:Exchangerを介してオブジェクトを正常に交換するスレッドのペアごとに、各スレッドのexchange()の前のアクションが発生します。つまり、他のスレッドの対応するexchange()からの戻りの後にアクションが発生します。

最善の解決策は何だと思いますか?

どちらも同等です。表現力をターゲットにする必要があります。同期/ロック/モニターベースのソリューションは、交換ベースのソリューションよりも表現力に富んでいます。ただし、このコードを専用のクラスで抽象化するかどうかは実際には問題ではありません。

私はそれを正しくやっていますか、それとも別の明白な解決策を見逃していますか?

AFAIKいいえホイールを再実装したくない場合。

ReentrantLockベースのソリューションは、昔ながらの同期またはGuavaのMonitorを使用して作成することもできることに注意してください。

比較については、 http: //docs.guava-libraries.googlecode.com/git-history/release/javadoc/com/google/common/util/concurrent/Monitor.htmlを参照してください。

そして、CyclicBarrierがコードを単純化していることに気づきませんでした...(実際、私はそれを使用する方法を完全には理解していませんでしたが、使用するよりも単純に見えませんでした

CyclicBarrierはあなたのニーズに合いません。相互排除を目的として設計されていません。これにより、一連のスレッドで共通のバリアを定義できます。スレッドは同時に実行され、次のステップに進む前に、ある時点で互いに待機します。

于 2013-03-10T19:31:42.153 に答える
0

ロック メカニズムは、ロックの相互排除に焦点を当てることで、ここで実行しているタスクに直接対処しているため、そのアプローチをお勧めします。

于 2013-03-10T16:41:21.710 に答える
0

まだ使用Exchangerしていませんが、達成したいことに対する最も簡単なソリューションのようです。はるかに一般的なLock/Conditionバージョンよりも少ないコード。メモリの一貫性については、ここで約束されていることです。

于 2013-03-10T16:58:21.537 に答える