fix issue #343 ReaderWriterLock unit tests fail on CI

This commit is contained in:
hoswey 2016-01-13 09:35:31 +08:00
parent 150c4e0d5d
commit e95cfe9292
7 changed files with 131 additions and 86 deletions

View File

@ -18,8 +18,7 @@ import java.util.stream.IntStream;
* readers will be blocked until the writer is finished writing.
*
* <p>
* This example use two mutex to demonstrate the concurrent access of multiple readers and
* writers.
* This example use two mutex to demonstrate the concurrent access of multiple readers and writers.
*
*
* @author hongshuwei@gmail.com
@ -33,15 +32,15 @@ public class App {
*/
public static void main(String[] args) {
ExecutorService executeService = Executors.newFixedThreadPool(1000);
ExecutorService executeService = Executors.newFixedThreadPool(10);
ReaderWriterLock lock = new ReaderWriterLock();
// Start 10 readers
IntStream.range(0, 10)
// Start 5 readers
IntStream.range(0, 5)
.forEach(i -> executeService.submit(new Reader("Reader " + i, lock.readLock())));
// Start 10 writers
IntStream.range(0, 10)
// Start 5 writers
IntStream.range(0, 5)
.forEach(i -> executeService.submit(new Writer("Writer " + i, lock.writeLock())));
// In the system console, it can see that the read operations are executed concurrently while
// write operations are exclusive.

View File

@ -34,7 +34,7 @@ public class Reader implements Runnable {
*/
public void read() throws InterruptedException {
System.out.println(name + " begin");
Thread.sleep(100);
Thread.sleep(250);
System.out.println(name + " finish");
}
}

View File

@ -34,7 +34,7 @@ public class Writer implements Runnable {
*/
public void write() throws InterruptedException {
System.out.println(name + " begin");
Thread.sleep(100);
Thread.sleep(250);
System.out.println(name + " finish");
}
}

View File

@ -1,87 +1,81 @@
package com.iluwatar.reader.writer.lock;
import static org.mockito.Mockito.after;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.inOrder;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.InOrder;
/**
* @author hongshuwei@gmail.com
*/
public class ReaderAndWriterTest {
public class ReaderAndWriterTest extends StdOutTest {
ExecutorService executeService;
@Before
public void setup() {
executeService = Executors.newFixedThreadPool(2);
}
/**
* Verify reader and writer can only get the lock to read and write orderly
*/
@Ignore // intermittent failures when executed on CI
@Test
public void testReadAndWrite() throws Exception {
ReaderWriterLock lock = new ReaderWriterLock();
Reader reader1 = spy(new Reader("Reader 1", lock.readLock()));
Writer writer1 = spy(new Writer("Writer 1", lock.writeLock()));
executeService.submit(reader1);
// Let reader1 execute first
Thread.sleep(50);
executeService.submit(writer1);
verify(reader1, timeout(99).atLeastOnce()).read();
verify(writer1, after(10).never()).write();
verify(writer1, timeout(100).atLeastOnce()).write();
}
/**
* Verify reader and writer can only get the lock to read and write orderly
*/
@Ignore // intermittent failures when executed on CI
@Test
public void testWriteAndRead() throws Exception {
Reader reader1 = new Reader("Reader 1", lock.readLock());
Writer writer1 = new Writer("Writer 1", lock.writeLock());
ExecutorService executeService = Executors.newFixedThreadPool(2);
ReaderWriterLock lock = new ReaderWriterLock();
Reader reader1 = spy(new Reader("Reader 1", lock.readLock()));
Writer writer1 = spy(new Writer("Writer 1", lock.writeLock()));
executeService.submit(writer1);
// Let reader1 execute first
Thread.sleep(50);
executeService.submit(reader1);
// Let reader1 execute first
Thread.sleep(150);
executeService.submit(writer1);
verify(writer1, timeout(99).atLeastOnce()).write();
verify(reader1, after(10).never()).read();
verify(reader1, timeout(100).atLeastOnce()).read();
}
@After
public void tearDown() {
executeService.shutdown();
try {
executeService.awaitTermination(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
System.out.println("Error waiting for ExecutorService shutdown");
}
final InOrder inOrder = inOrder(getStdOutMock());
inOrder.verify(getStdOutMock()).println("Reader 1 begin");
inOrder.verify(getStdOutMock()).println("Reader 1 finish");
inOrder.verify(getStdOutMock()).println("Writer 1 begin");
inOrder.verify(getStdOutMock()).println("Writer 1 finish");
}
/**
* Verify reader and writer can only get the lock to read and write orderly
*/
@Test
public void testWriteAndRead() throws Exception {
ExecutorService executeService = Executors.newFixedThreadPool(2);
ReaderWriterLock lock = new ReaderWriterLock();
Reader reader1 = new Reader("Reader 1", lock.readLock());
Writer writer1 = new Writer("Writer 1", lock.writeLock());
executeService.submit(writer1);
// Let writer1 execute first
Thread.sleep(150);
executeService.submit(reader1);
executeService.shutdown();
try {
executeService.awaitTermination(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
System.out.println("Error waiting for ExecutorService shutdown");
}
final InOrder inOrder = inOrder(getStdOutMock());
inOrder.verify(getStdOutMock()).println("Writer 1 begin");
inOrder.verify(getStdOutMock()).println("Writer 1 finish");
inOrder.verify(getStdOutMock()).println("Reader 1 begin");
inOrder.verify(getStdOutMock()).println("Reader 1 finish");
}
}

View File

@ -1,25 +1,23 @@
package com.iluwatar.reader.writer.lock;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.InOrder;
/**
* @author hongshuwei@gmail.com
*/
public class ReaderTest {
public class ReaderTest extends StdOutTest {
/**
* Verify that multiple readers can get the read lock concurrently
*/
@Ignore // intermittent failures when executed on CI
@Test
public void testRead() throws Exception {
@ -30,18 +28,23 @@ public class ReaderTest {
Reader reader2 = spy(new Reader("Reader 2", lock.readLock()));
executeService.submit(reader1);
Thread.sleep(150);
executeService.submit(reader2);
// Read operation will hold the read lock 100 milliseconds, so here we guarantee that each
// readers can read in 99 milliseconds to prove that multiple read can perform in the same time.
verify(reader1, timeout(99).atLeastOnce()).read();
verify(reader2, timeout(99).atLeastOnce()).read();
executeService.shutdown();
try {
executeService.awaitTermination(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
System.out.println("Error waiting for ExecutorService shutdown");
}
// Read operation will hold the read lock 250 milliseconds, so here we prove that multiple reads
// can be performed in the same time.
final InOrder inOrder = inOrder(getStdOutMock());
inOrder.verify(getStdOutMock()).println("Reader 1 begin");
inOrder.verify(getStdOutMock()).println("Reader 2 begin");
inOrder.verify(getStdOutMock()).println("Reader 1 finish");
inOrder.verify(getStdOutMock()).println("Reader 2 finish");
}
}

View File

@ -0,0 +1,53 @@
package com.iluwatar.reader.writer.lock;
import org.junit.After;
import org.junit.Before;
import java.io.PrintStream;
import static org.mockito.Mockito.mock;
/**
* Date: 12/10/15 - 8:37 PM
*
* @author Jeroen Meulemeester
*/
public abstract class StdOutTest {
/**
* The mocked standard out {@link PrintStream}, required since some actions don't have any
* influence on accessible objects, except for writing to std-out using {@link System#out}
*/
private final PrintStream stdOutMock = mock(PrintStream.class);
/**
* Keep the original std-out so it can be restored after the test
*/
private final PrintStream stdOutOrig = System.out;
/**
* Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test
*/
@Before
public void setUp() {
System.setOut(this.stdOutMock);
}
/**
* Removed the mocked std-out {@link PrintStream} again from the {@link System} class
*/
@After
public void tearDown() {
System.setOut(this.stdOutOrig);
}
/**
* Get the mocked stdOut {@link PrintStream}
*
* @return The stdOut print stream mock, renewed before each test
*/
final PrintStream getStdOutMock() {
return this.stdOutMock;
}
}

View File

@ -1,26 +1,23 @@
package com.iluwatar.reader.writer.lock;
import static org.mockito.Mockito.after;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.InOrder;
/**
* @author hongshuwei@gmail.com
*/
public class WriterTest {
public class WriterTest extends StdOutTest {
/**
* Verify that multiple writers will get the lock in order.
*/
@Ignore // intermittent failures when executed on CI
@Test
public void testWrite() throws Exception {
@ -32,23 +29,22 @@ public class WriterTest {
executeService.submit(writer1);
// Let write1 execute first
Thread.sleep(50);
Thread.sleep(150);
executeService.submit(writer2);
// Write operation will hold the write lock 100 milliseconds, so here we verify that when two
// write excute concurrently
// 1. The first write will get the lock and and write in 60ms
// 2. The second writer will cannot get the lock when first writer get the lock
// 3. The second writer will get the lock as last
verify(writer1, timeout(10).atLeastOnce()).write();
verify(writer2, after(10).never()).write();
verify(writer2, timeout(100).atLeastOnce()).write();
executeService.shutdown();
try {
executeService.awaitTermination(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
System.out.println("Error waiting for ExecutorService shutdown");
}
// Write operation will hold the write lock 250 milliseconds, so here we verify that when two
// writer execute concurrently, the second writer can only writes only when the first one is
// finished.
final InOrder inOrder = inOrder(getStdOutMock());
inOrder.verify(getStdOutMock()).println("Writer 1 begin");
inOrder.verify(getStdOutMock()).println("Writer 1 finish");
inOrder.verify(getStdOutMock()).println("Writer 2 begin");
inOrder.verify(getStdOutMock()).println("Writer 2 finish");
}
}