Предположения
- эффективный означает, что
doGet()
должен завершиться как можно быстрее
cachedPageIsStale()
совсем не требует времени
downloadNewVersionOfResource()
занимает немного времени
Ответ
Синхронизация уменьшает нагрузку на сеть, потому что только один поток извлекает ресурс, когда он истекает. Кроме того, это не приведет к чрезмерной задержке обработки других потоков - поскольку виртуальная машина не содержит текущего снимка, который могут вернуть потоки, им придется блокировать, и нет никаких причин, по которым дополнительный параллельный downloadNewVersionOfResource()
будет завершаться быстрее ( ожидайте обратного из-за конкуренции с сетью).
Таким образом, синхронизация хороша и оптимальна для полосы пропускания и времени отклика. (Затраты ЦП на синхронизацию исчезающе малы по сравнению с ожиданиями ввода-вывода) - при условии, что текущая версия ресурса может быть недоступна при вызове doGet (); если ваш сервер всегда имел текущую версию ресурса, он мог бы отправить его обратно без задержки. (Возможно, фоновая ветка скачает новую версию незадолго до истечения срока действия старой.)
PS
Вы не показали никакой обработки ошибок. Вам нужно будет решить, распространять ли исключительные ситуации, генерируемые функцией downloadNewVersionOfResource (), вашим абонентам или продолжать обслуживать старую версию ресурса.
Редактировать
Так? Предположим, у вас есть 100 рабочих соединений, и проверка того, является ли ресурс устаревшим, занимает одну микросекунду, ресурс не устарел, а его обслуживание занимает одну секунду. Затем в среднем 100 * 10 ^ -6 / 1 = 0,0001 потоки пытаются получить блокировку. Едва ли вообще разногласия. И накладные расходы на получение неиспользованной блокировки составляют порядка 10-8 секунд. Нет смысла оптимизировать вещи, которые уже принимают микросенды, когда сеть вызовет задержки в миллисекунды. И если вы мне не верите, сделайте микробенчмарк для синхронизации. Это правда, что частая ненужная синхронизация приводит к значительным накладным расходам, и что синхронизация классов сбора по этой причине устарела. Но это потому, что эти методы выполняют очень мало работы за вызов, а относительная нагрузка на синхронизацию была намного больше. Я только что сделал небольшой микробенчмарк для следующего кода:
synchronized (lock) {
c++;
}
На моем ноутбуке это занимает 50 наносекунд (5 * 10 ^ -8 секунд) в среднем за 10 миллионов выполнений в солнечной точке доступа vm. Это примерно в 20 раз дольше, чем операция «голого» приращения, поэтому, если сделать много приращений, синхронизация каждого из них приведет к замедлению программы на порядок. Однако если бы этот метод блокировал ввод / вывод, ожидая, скажем, 1 мс, добавление тех же 50 наносекунд уменьшило бы пропускную способность на 0,005%. Наверняка у вас больше возможностей для настройки производительности: -)
Вот почему вы всегда должны измерять перед началом оптимизации. Это не позволяет вам тратить часы своего времени, чтобы сэкономить процессорное время на пару наносекунд.