// key: url, value: list of waiting latches for threads
private static final Map<String, List<CountDownLatch>> waiters;
По входу нового потока идёт проверка waiters на наличие списка по url:
если записи нет (waiters.get(url) == null), то создаётся новый пустой список и помещается в Map, сам поток начинает делать работу;
если запись есть, то данный поток создаёт объект типа CountDownLatch с параметром 1, добавляет его в список и вызывает метод await().
Тот поток, который получил право на выполнение работы по окончанию проверяет Map на наличие списка ждущих потоков, удаляет его из Map'a и вызывает методы count() для каждого latch'а.
Все операции с waiters — должны быть синхронизованы:
synchronized(waiters) {
// check if record for url exists and either continue work or wait
// ...
}
// do work
synchronized(waiters) {
// check if record for url and call count() for all waiters, then remove record
// ...
}
Вроде решает поставленную вами задачу.