更新:请参阅此问题的底部以获得完整答案.
我想运行一个辅助线程,以便我的主线程和我的辅助线程交替执行操作(不,我不想在主线程中执行所有操作,它用于单元测试).
我提出了两个不同的解决方案,我不知道哪个是最好的,我对第一个问题有疑问:
我使用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();
}
}
Run Code Online (Sandbox Code Playgroud)
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();
}
}
Run Code Online (Sandbox Code Playgroud)
在我看来有点复杂.
您认为什么是最佳解决方案?我做得对吗,还是我错过了另一个明显的解决方案?
我没有使用a,CountDownLatch因为我想要交替运行多个操作,并且CountDownLatch无法重置.而且我没有发现CyclicBarrier使代码更简单...(实际上我并不完全理解如何使用它,但它看起来并不比使用Exchanger或简单Condition)
谢谢.
@ClémentMATHIE提供了不同的如何实现这一点的例子,在其接受的答案的评论中,请参阅:https://gist.github.com/cykl/5131021
有三个例子,一个使用a CyclicBarrier,另一个使用a ,Exchanger最后一个使用2 Semaphores.虽然他说"更具表现力的是基于信号量的信号量"是正确的,但我选择使用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();
}
}
Run Code Online (Sandbox Code Playgroud)
我是否正确地认为交换方法执行内存同步,就像使用锁一样?
你是对的。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 不符合您的需求。它不是为相互排斥而设计的;它允许一组线程定义一个公共屏障。线程将同时执行并在进入下一步之前的某个时刻相互等待。