Для чего в приведённом коде ConcurrentHashMap?

Сам я PHP-разработчик, а на джаве решил пописать для души. Пишу простенькое десктоп-приложение с JavaFX, понадобилось реализовать Debounce для слайдера. Нашёл на StackOverflow такое решение:
public class Debouncer <T> {
  private final ScheduledExecutorService sched = Executors.newScheduledThreadPool(1);
  private final ConcurrentHashMap<T, TimerTask> delayedMap = new ConcurrentHashMap<T, TimerTask>();
  private final Callback<T> callback;
  private final int interval;

  public Debouncer(Callback<T> c, int interval) { 
    this.callback = c;
    this.interval = interval;
  }

  public void call(T key) {
    TimerTask task = new TimerTask(key);

    TimerTask prev;
    do {
      prev = delayedMap.putIfAbsent(key, task);
      if (prev == null)
        sched.schedule(task, interval, TimeUnit.MILLISECONDS);
    } while (prev != null && !prev.extend()); // Exit only if new task was added to map, or existing task was extended successfully
  }
  
  public void terminate() {
    sched.shutdownNow();
  }
  
  // The task that wakes up when the wait time elapses
  private class TimerTask implements Runnable {
    private final T key;
    private long dueTime;    
    private final Object lock = new Object();

    public TimerTask(T key) {        
      this.key = key;
      extend();
    }

    public boolean extend() {
      synchronized (lock) {
        if (dueTime < 0) // Task has been shutdown
          return false;
        dueTime = System.currentTimeMillis() + interval;
        return true;
      }
    }
      
    public void run() {
      synchronized (lock) {
        long remaining = dueTime - System.currentTimeMillis();
        if (remaining > 0) { // Re-schedule task
          sched.schedule(this, remaining, TimeUnit.MILLISECONDS);
        } else { // Mark as terminated and invoke callback
          dueTime = -1;
          try {
            callback.call(key);
          } finally {
            delayedMap.remove(key);
          }
        }
      }
    }  
  }
}


Начал разбираться в коде и первое, на что обратил внимание - зачем здесь при вызове метода call передавать ключ, если в классе 1 неизменяемый коллбэк на инстанс? То есть, переиспользовать этот класс с другой логикой можно только созданием нового инстанса. Тут, допустим, я понял - чтобы был ключ для мапы. Но тогда второй вопрос: зачем сама мапа нужна? Для упрощения логики с заменой таска в call и сохранения потокобезопасности?

P.S. сама страничка на SO: https://stackoverflow.com/questions/4742210/implem...
  • Вопрос задан
  • 82 просмотра
Пригласить эксперта
Ответы на вопрос 1
dollar
@dollar
Делай добро и бросай его в воду.
ConcurrentHashMap в этом коде служит нескольким целям:

1. Потокобезопасность: `ConcurrentHashMap` обеспечивает безопасное выполнение нескольких операций в многопоточной среде без необходимости внешней синхронизации.

2. Управление задачами: Мапа хранит задачи (`TimerTask`), которые должны быть выполнены. Ключ в мапе позволяет идентифицировать и управлять конкретными задачами. Если новая задача приходит с тем же ключом, старая задача может быть либо заменена, либо "продлена", в зависимости от логики в методе `call`.

3. Гранулярность блокировки: комментарий автора кода указывает на это. Блокировка на уровне ключа позволяет разным потокам работать с разными ключами одновременно, не блокируя друг друга.

Таким образом, мапа здесь служит для хранения задач, ассоциированных с определенными ключами, и обеспечения потокобезопасного доступа и управления этими задачами.

Что касается вашего вопроса о ключе: ключ здесь действительно нужен для идентификации задачи в мапе. Если бы ключа не было, невозможно было бы управлять отдельными задачами. Сам класс `Debouncer` действительно имеет один коллбек, но он может управлять множеством задач, каждая из которых идентифицируется ключом.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы