public synchronized V get(final K key, final Supplier<V> valueSupplier, final Consumer<V> expireCallback) {
        final var value = cache.get(key);
        if (value != null) {
            value.updateDeadline();
            return value.value;
        }

        final var newValue = new ExpirableValue<>(valueSupplier.get(), expireCallback);
        newValue.updateDeadline();
        cache.put(key, newValue);
        return newValue.value;
    }
@Test
    public void testExpire() throws InterruptedException {
        final var cache = new SimpleCache<Integer, Integer>(executor, 500L, 5);
        final var expiredValues = Collections.synchronizedSet(new HashSet<Integer>());

        final var allKeys = IntStream.range(0, 5).boxed().collect(Collectors.toSet());
        allKeys.forEach(key -> cache.get(key, () -> key + 100, expiredValues::add));

        Thread.sleep(400L);
        final var recentAccessedKey = Set.of(1, 2);
        recentAccessedKey.forEach(key -> cache.get(key, () -> -1, expiredValues::add)); // access these keys

        Thread.sleep(300L);
        recentAccessedKey.forEach(key -> Assert.assertEquals(key + 100, cache.get(key, () -> -1, __ -> {})));
        allKeys.stream().filter(key -> !recentAccessedKey.contains(key))
                .forEach(key -> Assert.assertEquals(-1, cache.get(key, () -> -1, __ -> {})));
    }