fix issue #343 ReaderWriterLock unit tests fail on CI
This commit is contained in:
parent
150c4e0d5d
commit
e95cfe9292
@ -18,8 +18,7 @@ import java.util.stream.IntStream;
|
|||||||
* readers will be blocked until the writer is finished writing.
|
* readers will be blocked until the writer is finished writing.
|
||||||
*
|
*
|
||||||
* <p>
|
* <p>
|
||||||
* This example use two mutex to demonstrate the concurrent access of multiple readers and
|
* This example use two mutex to demonstrate the concurrent access of multiple readers and writers.
|
||||||
* writers.
|
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @author hongshuwei@gmail.com
|
* @author hongshuwei@gmail.com
|
||||||
@ -33,15 +32,15 @@ public class App {
|
|||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
||||||
ExecutorService executeService = Executors.newFixedThreadPool(1000);
|
ExecutorService executeService = Executors.newFixedThreadPool(10);
|
||||||
ReaderWriterLock lock = new ReaderWriterLock();
|
ReaderWriterLock lock = new ReaderWriterLock();
|
||||||
|
|
||||||
// Start 10 readers
|
// Start 5 readers
|
||||||
IntStream.range(0, 10)
|
IntStream.range(0, 5)
|
||||||
.forEach(i -> executeService.submit(new Reader("Reader " + i, lock.readLock())));
|
.forEach(i -> executeService.submit(new Reader("Reader " + i, lock.readLock())));
|
||||||
|
|
||||||
// Start 10 writers
|
// Start 5 writers
|
||||||
IntStream.range(0, 10)
|
IntStream.range(0, 5)
|
||||||
.forEach(i -> executeService.submit(new Writer("Writer " + i, lock.writeLock())));
|
.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
|
// In the system console, it can see that the read operations are executed concurrently while
|
||||||
// write operations are exclusive.
|
// write operations are exclusive.
|
||||||
|
@ -34,7 +34,7 @@ public class Reader implements Runnable {
|
|||||||
*/
|
*/
|
||||||
public void read() throws InterruptedException {
|
public void read() throws InterruptedException {
|
||||||
System.out.println(name + " begin");
|
System.out.println(name + " begin");
|
||||||
Thread.sleep(100);
|
Thread.sleep(250);
|
||||||
System.out.println(name + " finish");
|
System.out.println(name + " finish");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ public class Writer implements Runnable {
|
|||||||
*/
|
*/
|
||||||
public void write() throws InterruptedException {
|
public void write() throws InterruptedException {
|
||||||
System.out.println(name + " begin");
|
System.out.println(name + " begin");
|
||||||
Thread.sleep(100);
|
Thread.sleep(250);
|
||||||
System.out.println(name + " finish");
|
System.out.println(name + " finish");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,87 +1,81 @@
|
|||||||
package com.iluwatar.reader.writer.lock;
|
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.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mockito.InOrder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author hongshuwei@gmail.com
|
* @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
|
* Verify reader and writer can only get the lock to read and write orderly
|
||||||
*/
|
*/
|
||||||
@Ignore // intermittent failures when executed on CI
|
|
||||||
@Test
|
@Test
|
||||||
public void testReadAndWrite() throws Exception {
|
public void testReadAndWrite() throws Exception {
|
||||||
|
|
||||||
ReaderWriterLock lock = new ReaderWriterLock();
|
ReaderWriterLock lock = new ReaderWriterLock();
|
||||||
|
|
||||||
Reader reader1 = spy(new Reader("Reader 1", lock.readLock()));
|
Reader reader1 = new Reader("Reader 1", lock.readLock());
|
||||||
Writer writer1 = spy(new Writer("Writer 1", lock.writeLock()));
|
Writer writer1 = 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 {
|
|
||||||
|
|
||||||
ExecutorService executeService = Executors.newFixedThreadPool(2);
|
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);
|
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();
|
executeService.shutdown();
|
||||||
try {
|
try {
|
||||||
executeService.awaitTermination(10, TimeUnit.SECONDS);
|
executeService.awaitTermination(10, TimeUnit.SECONDS);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
System.out.println("Error waiting for ExecutorService shutdown");
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,25 +1,23 @@
|
|||||||
package com.iluwatar.reader.writer.lock;
|
package com.iluwatar.reader.writer.lock;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.inOrder;
|
||||||
import static org.mockito.Mockito.spy;
|
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.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mockito.InOrder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author hongshuwei@gmail.com
|
* @author hongshuwei@gmail.com
|
||||||
*/
|
*/
|
||||||
public class ReaderTest {
|
public class ReaderTest extends StdOutTest {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify that multiple readers can get the read lock concurrently
|
* Verify that multiple readers can get the read lock concurrently
|
||||||
*/
|
*/
|
||||||
@Ignore // intermittent failures when executed on CI
|
|
||||||
@Test
|
@Test
|
||||||
public void testRead() throws Exception {
|
public void testRead() throws Exception {
|
||||||
|
|
||||||
@ -30,18 +28,23 @@ public class ReaderTest {
|
|||||||
Reader reader2 = spy(new Reader("Reader 2", lock.readLock()));
|
Reader reader2 = spy(new Reader("Reader 2", lock.readLock()));
|
||||||
|
|
||||||
executeService.submit(reader1);
|
executeService.submit(reader1);
|
||||||
|
Thread.sleep(150);
|
||||||
executeService.submit(reader2);
|
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();
|
executeService.shutdown();
|
||||||
try {
|
try {
|
||||||
executeService.awaitTermination(10, TimeUnit.SECONDS);
|
executeService.awaitTermination(10, TimeUnit.SECONDS);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
System.out.println("Error waiting for ExecutorService shutdown");
|
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");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,26 +1,23 @@
|
|||||||
package com.iluwatar.reader.writer.lock;
|
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.spy;
|
||||||
import static org.mockito.Mockito.timeout;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
|
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mockito.InOrder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author hongshuwei@gmail.com
|
* @author hongshuwei@gmail.com
|
||||||
*/
|
*/
|
||||||
public class WriterTest {
|
public class WriterTest extends StdOutTest {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify that multiple writers will get the lock in order.
|
* Verify that multiple writers will get the lock in order.
|
||||||
*/
|
*/
|
||||||
@Ignore // intermittent failures when executed on CI
|
|
||||||
@Test
|
@Test
|
||||||
public void testWrite() throws Exception {
|
public void testWrite() throws Exception {
|
||||||
|
|
||||||
@ -32,23 +29,22 @@ public class WriterTest {
|
|||||||
|
|
||||||
executeService.submit(writer1);
|
executeService.submit(writer1);
|
||||||
// Let write1 execute first
|
// Let write1 execute first
|
||||||
Thread.sleep(50);
|
Thread.sleep(150);
|
||||||
executeService.submit(writer2);
|
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();
|
executeService.shutdown();
|
||||||
try {
|
try {
|
||||||
executeService.awaitTermination(10, TimeUnit.SECONDS);
|
executeService.awaitTermination(10, TimeUnit.SECONDS);
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
System.out.println("Error waiting for ExecutorService shutdown");
|
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user