Для чего в приведённом коде 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...
  • Вопрос задан
  • 77 просмотров
Пригласить эксперта
Ответы на вопрос 1
dollar
@dollar
Делай добро и бросай его в воду.
ConcurrentHashMap в этом коде служит нескольким целям:

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

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

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

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

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

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

Войти через центр авторизации
Похожие вопросы