更新:请查看问题底部,以获得完整的答案。
我想运行一个辅助线程,使我的主线程和辅助线程交替执行操作(不,我不想在主线程中执行所有操作,这是为了一个单元测试)。
我想出了两种不同的解决方案,但我不知道哪个是最好的,并且对第一个解决方案有一些疑问:
使用 Exchanger
我想到了使用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 {
//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
一样具有内存同步的功能?
使用条件
另一种使用条件的解决方案:
@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
,因为我想交替运行多个操作,而 CountDownLatch
不能被重置。而且我并没有发现使用 CyclicBarrier
会使代码更简单...(实际上,我并没有完全理解如何使用它,但它看起来不比使用 Exchanger
或 Condition
更简单)
谢谢。
更新
@Clément MATHIEU 在其已接受的答案的评论中提供了不同的实现示例,请参见:https://gist.github.com/cykl/5131021
这里有三个示例,一个使用 CyclicBarrier
,另一个使用 Exchanger
,最后一个使用 2 个 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();
}
}
Exchanger
,它的作用是阻止两个线程之一访问它,直到另一个尝试交换,然后它们交换项目,然后再次运行。所以它绝对不会做你想做的事情。我猜你实际上想要一对普通的信号量。也就是说,当线程T1完成操作时,它会发出S2信号,在S1上等待,反之亦然。 - millimoose