class Throttled {

    public void trigger(Key key, Runnable r) {
        final Runnable wrappedRunnable = () -> {
            try {
                r.run();
            } finally {
                final Instant after = Instant.now();
                synchronized (actionMap) {
                    actionMap.put(key, Action.toBeExecutedSomeTimeLater(after));
                }
            }
        };

        final Instant now = Instant.now();
        synchronized (actionMap) {
            final Action action = actionMap.get(key);
            if (action != null) {
                if (!action.alreadyScheduled) {
                    final Instant lastTime = action.getExecutionTime();
                    final Duration between = Duration.between(lastTime, now);
                    if (between.getSeconds() < minIntervalInSeconds) {
                        final long delay = minIntervalInSeconds - between.getSeconds();
                        actionMap.put(key, action.scheduled());
                        scheduledExecutor.schedule(wrappedRunnable, delay, TimeUnit.SECONDS);
                    } else {
                        actionMap.put(key, action.scheduled());
                        scheduledExecutor.execute(wrappedRunnable);
                    }
                }
            } else {
                actionMap.put(key, Action.toBeExecutedASAP(now));
                scheduledExecutor.execute(wrappedRunnable);
            }
        }
    }

    public  Throttled(long minIntervalInSeconds);

}