@Test public void shouldFetchOnlyOnceWhenMultipleThreadsTryToGetKeys() throws Exception { final KeysDownloader instance = newInstanceForTests(); httpResponseBuilder = new HttpResponseBuilder().setContent("keys"); List<FutureTask<String>> tasks = new ArrayList<>(); for (int i = 0; i < 10; i++) { tasks.add( new FutureTask<String>( new Callable<String>() { @Override public String call() throws Exception { return instance.download(); } })); } delayHttpResponseLatch = new CountDownLatch(1); for (FutureTask<String> task : tasks) { executor.execute(task); } delayHttpResponseLatch.countDown(); for (FutureTask<String> task : tasks) { assertEquals("keys", task.get(5, TimeUnit.SECONDS)); } assertEquals(1, httpTransportGetCount.get()); }
public String download() throws IOException { synchronized (instanceStateLock) { if (hasNonExpiredDataCached()) { if (shouldProactivelyRefreshDataInBackground()) { refreshInBackground(); } return cachedData; } } synchronized (fetchDataLock) { synchronized (instanceStateLock) { if (hasNonExpiredDataCached()) { return cachedData; } } return fetchAndCacheData(); } }
KeysDownloader { public String download() throws IOException { synchronized (instanceStateLock) { if (hasNonExpiredDataCached()) { if (shouldProactivelyRefreshDataInBackground()) { refreshInBackground(); } return cachedData; } } synchronized (fetchDataLock) { synchronized (instanceStateLock) { if (hasNonExpiredDataCached()) { return cachedData; } } return fetchAndCacheData(); } } }
KeysDownloader { public String download() throws IOException { synchronized (instanceStateLock) { if (hasNonExpiredDataCached()) { if (shouldProactivelyRefreshDataInBackground()) { refreshInBackground(); } return cachedData; } } synchronized (fetchDataLock) { synchronized (instanceStateLock) { if (hasNonExpiredDataCached()) { return cachedData; } } return fetchAndCacheData(); } } KeysDownloader(Executor backgroundExecutor, HttpTransport httpTransport, String url); }
KeysDownloader { public String download() throws IOException { synchronized (instanceStateLock) { if (hasNonExpiredDataCached()) { if (shouldProactivelyRefreshDataInBackground()) { refreshInBackground(); } return cachedData; } } synchronized (fetchDataLock) { synchronized (instanceStateLock) { if (hasNonExpiredDataCached()) { return cachedData; } } return fetchAndCacheData(); } } KeysDownloader(Executor backgroundExecutor, HttpTransport httpTransport, String url); String download(); HttpTransport getHttpTransport(); String getUrl(); void refreshInBackground(); }
KeysDownloader { public String download() throws IOException { synchronized (instanceStateLock) { if (hasNonExpiredDataCached()) { if (shouldProactivelyRefreshDataInBackground()) { refreshInBackground(); } return cachedData; } } synchronized (fetchDataLock) { synchronized (instanceStateLock) { if (hasNonExpiredDataCached()) { return cachedData; } } return fetchAndCacheData(); } } KeysDownloader(Executor backgroundExecutor, HttpTransport httpTransport, String url); String download(); HttpTransport getHttpTransport(); String getUrl(); void refreshInBackground();  }