线程同步
1、在线程上调用wait()方法,并使用while循环;
while(someCondition){
try {
wait();
} catch (InterruptedException e) {
//In this case we don't care, but we may want
//to propagate with Thread.interrupt()
}
}
2、notifyAll()唤醒阻塞的线程;
JDK1.5引入ReentrantLock
1、使用ReentrantLock.newCondition(),来实现更精细粒度的控制;
2、Condition.signalAll()
while(list.isEmpty()){
Condition.await();
}
Monitor
Guava的Monitor类为我们提供了一个允许多种条件的解决方案,并且完全消除了显式notify线程的方式。
public class MonitorSample {
private List<String> list = new ArrayList<String>();
private static final int MAX_SIZE = 10;
private Monitor monitor = new Monitor();
private Monitor.Guard listBelowCapacity = new Monitor.Guard(monitor) {
@Override
public boolean isSatisfied() {
return list.size() < MAX_SIZE;
}
};
public void addToList(String item) throws InterruptedException {
monitor.enterWhen(listBelowCapacity);
try {
list.add(item);
} finally {
monitor.leave();
}
}
}
任何时候都只有一个Thread进入Monitor块,语义类似synchronized和ReentrantLock
最佳实践
// 条件判断是否进入阻塞块
if (monitor.enterIf(guardCondition)) {
try {
doWork();
} finally {
monitor.leave();
}
}
// 进入阻塞&满足condition
monitor.enterWhen(guardCondition);
try {
doWork();
} finally {
monitor.leave()
}
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.lang.reflect.Method;
import java.util.concurrent.*;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
public class MonitorExampleTest {
private MonitorExample monitorExample;
private ExecutorService executorService;
private int numberThreads = 10;
private CountDownLatch startSignal;
private CountDownLatch doneSignal;
@Before
public void setUp() throws Exception {
monitorExample = new MonitorExample();
executorService = Executors.newFixedThreadPool(numberThreads);
startSignal = new CountDownLatch(1);
doneSignal = new CountDownLatch(numberThreads);
}
@After
public void tearDown() {
executorService.shutdownNow();
}
/*
* First thread does some simulated work and the following
* 9 threads will move on.
*/
@Test
public void testDemoTryEnterIf() throws Exception {
setUpThreadsForTestingMethod("demoTryEnterIf");
startAllThreadsForTest();
waitForTestThreadsToFinish();
int expectedTaskCount = 1;
int expectedSkippedTasks = 9;
assertThat(monitorExample.getTaskDoneCounter(), is(expectedTaskCount));
assertThat(monitorExample.getTaskSkippedCounter(), is(expectedSkippedTasks));
}
/*
The first 5 threads will wait for the monitor because
the guard condition is true, but once it turns false the
rest of the threads drop off
*/
@Test
public void testDemoEnterIfOnlyFiveTasksComplete() throws Exception {
monitorExample.setStopTaskCount(5);
setUpThreadsForTestingMethod("demoEnterIf");
startAllThreadsForTest();
waitForTestThreadsToFinish();
int expectedTaskCount = 5;
int expectedSkippedTasks = 5;
assertThat(monitorExample.getTaskDoneCounter(), is(expectedTaskCount));
assertThat(monitorExample.getTaskSkippedCounter(), is(expectedSkippedTasks));
}
/*
All 10 threads enter the monitor as the guard condition
remains true the entire time.
*/
@Test
public void testDemoEnterIfAllTasksComplete() throws Exception {
monitorExample.setStopTaskCount(Integer.MAX_VALUE);
setUpThreadsForTestingMethod("demoEnterIf");
startAllThreadsForTest();
waitForTestThreadsToFinish();
int expectedTaskCount = 10;
int expectedSkippedTasks = 0;
assertThat(monitorExample.getTaskDoneCounter(), is(expectedTaskCount));
assertThat(monitorExample.getTaskSkippedCounter(), is(expectedSkippedTasks));
}
/*
Guard condition is initially false, but all 10 threads
enter the monitor.
*/
@Test
public void testDemoEnterWhen() throws Exception {
monitorExample.setStopTaskCount(Integer.MAX_VALUE);
monitorExample.setCondition(false);
setUpThreadsForTestingMethod("demoEnterWhen");
startAllThreadsForTest();
int expectedCompletedCount = 0;
int completedCount = monitorExample.getTaskDoneCounter();
assertThat(completedCount, is(expectedCompletedCount));
monitorExample.setCondition(true);
waitForTestThreadsToFinish();
expectedCompletedCount = 10;
completedCount = monitorExample.getTaskDoneCounter();
assertThat(completedCount, is(expectedCompletedCount));
}
private void waitForTestThreadsToFinish() throws InterruptedException {
doneSignal.await(1000l, TimeUnit.MILLISECONDS);
}
private void startAllThreadsForTest() {
startSignal.countDown();
}
private Method getMethodUnderTest(String methodName) throws Exception {
return monitorExample.getClass().getDeclaredMethod(methodName);
}
private void setUpThreadsForTestingMethod(String methodName) throws Exception {
final Method testMethod = getMethodUnderTest(methodName);
for (int i = 0; i < numberThreads; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
try {
startSignal.await();
testMethod.invoke(monitorExample);
} catch (Exception e) {
//Don't care
} finally {
doneSignal.countDown();
}
}
});
}
}
}