### Question:
Reader implements Runnable { public void read() throws InterruptedException { LOGGER.info("{} begin", name); Thread.sleep(readingTime); LOGGER.info("{} finish after reading {}ms", name, readingTime); } Reader(String name, Lock readLock, long readingTime);  Reader(String name, Lock readLock); @Override void run(); void read();  }### Answer:
@Test public void testRead() throws Exception { var executeService = Executors.newFixedThreadPool(2); var lock = new ReaderWriterLock(); var reader1 = spy(new Reader("Reader 1", lock.readLock())); var reader2 = spy(new Reader("Reader 2", lock.readLock())); executeService.submit(reader1); Thread.sleep(150); executeService.submit(reader2); executeService.shutdown(); try { executeService.awaitTermination(10, TimeUnit.SECONDS); } catch (InterruptedException e) { LOGGER.error("Error waiting for ExecutorService shutdown", e); } assertTrue(appender.logContains("Reader 1 begin")); assertTrue(appender.logContains("Reader 2 begin")); assertTrue(appender.logContains("Reader 1 finish")); assertTrue(appender.logContains("Reader 2 finish")); }
@Test public void testRead() throws Exception { ExecutorService executeService = Executors.newFixedThreadPool(2); ReaderWriterLock lock = new ReaderWriterLock(); Reader reader1 = spy(new Reader("Reader 1", lock.readLock())); Reader reader2 = spy(new Reader("Reader 2", lock.readLock())); executeService.submit(reader1); Thread.sleep(150); executeService.submit(reader2); executeService.shutdown(); try { executeService.awaitTermination(10, TimeUnit.SECONDS); } catch (InterruptedException e) { LOGGER.error("Error waiting for ExecutorService shutdown", e); } assertTrue(appender.logContains("Reader 1 begin")); assertTrue(appender.logContains("Reader 2 begin")); assertTrue(appender.logContains("Reader 1 finish")); assertTrue(appender.logContains("Reader 2 finish")); }