更新: 完全な回答については、この質問の下部を参照してください。
メイン スレッドとセカンダリ スレッドが交互に操作を実行できるように、セカンダリ スレッドを実行したい (いいえ、メイン スレッドですべての操作を実行する必要はありません。単体テスト用です)。
私は 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を使用するよりも単純に見えませんでした)CountDownLatch
CyclicBarrier
Exchanger
Condition
ありがとうございました。
アップデート
@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();
}
}