Может ли синхронизация внутреннего блока улучшить производительность уже синхронизированного метода?

Дана теоретическая система, в которой файлы 9X_threads загружаются из Интернета, если они не найдены 9X_threading в локальной системе, и предполагается:

  1. Механизм загрузки и извлечения/размещения из/в кэше (локальный файловая система) уже позаботились.
  2. Однопоточный и одиночный запрос на URL.

Я 9X_synchronized написал метод, использующий getFileFromLocalFS() и 9X_multithreading getFileFromWeb() для реализации упрощенной 9X_concurrent логики кеширования:

public InputStream getFile(String url) { // #1
    InputStream retStream = getFileFromLocalFS(url);
    if (retStream != null) {
        return retStream;           
    }
    else {
        retStream = getFileFromLocalFS(url);
        if (retStream == null) {
            return getFileFromWeb(url);
        }
    }
    return retStream;
}

Затем это схематическое 9X_openjdk решение необходимо было улучшить, чтобы 9X_concurrent оно соответствовало одновременным запросам на загрузку 9X_openjdk с одного и того же URL-адреса... и ограничивало 9X_java фактическую "из Интернета" одиночной загрузкой 9X_threading (т. е. все другие запросы будут получать 9X_concurency ее из локальной файловой системы) . Итак, я 9X_multithreading синхронизировал весь метод:

public synchronized InputStream getFile(String url) { // #2
    InputStream retStream = getFileFromLocalFS(url);
    if (retStream != null) {
        return retStream;           
    }
    else {
        retStream = getFileFromLocalFS(url);
        if (retStream == null) {
            return getFileFromWeb(url);
        }
    }
    return retStream;
}

Это, по сути, соответствует 9X_jdk запросу, но имеет проблему с производительностью, поскольку 9X_concurrency предотвращает запуск всего метода другим 9X_concurrency потоком до его завершения. То есть, даже 9X_javax если файл может быть извлечен из локальной 9X_multithread ФС, доступ к getFileFromLocalFS(url) невозможен, пока метод выполняется 9X_multithread другим потоком.

Улучшение производительности, предложенное 9X_threads моим интервьюером, заключалось в синхронизации 9X_concurrent блока getFileFromLocalFS(url):

public synchronized InputStream getFile(String url) { // #3
    InputStream retStream = getFileFromLocalFS(url);
    if (retStream != null) {
        return retStream;           
    }
    else {
        synchronized (this) { 
            retStream = getFileFromLocalFS(url);
            if (retStream == null) {
                return getFileFromWeb(url);
            }
        }
    }
    return retStream;
}

Я сказал "хорошо, но чтобы эта 9X_concurency оптимизация работала, нужно убрать синхронизацию 9X_concurrent-programming методов", т.е.:

public InputStream getFile(String url) { // #4
    InputStream retStream = getFileFromLocalFS(url);
    if (retStream != null) {
        return retStream;           
    }
    else {
        synchronized (this) { 
            retStream = getFileFromLocalFS(url);
            if (retStream == null) {
                return getFileFromWeb(url);
            }
        }
    }
    return retStream;
}

Интервьюер не согласился 9X_concurrent-programming и настоял на том, чтобы оставить обоих synchronized на месте.

Какой 9X_concurrent из них лучше работает в среде параллелизма? №3 9X_jdk или №4? Почему?

6
0
2
Общее количество ответов: 2

Ответ #1

Ответ на вопрос: Может ли синхронизация внутреннего блока улучшить производительность уже синхронизированного метода?

Кажется, происходит Cargo Cult Programming. Третий вариант напоминает 9X_openjdk Double-Checked Locking, но не улавливает сути. Но что еще более 9X_jdk поразительно, первый вариант также напоминает Double-Checked 9X_jre Locking, вызывая getFileFromLocalFS(url) дважды без причины. И 9X_threading это отправная точка…

Ваше подозрение относительно 9X_threading «улучшения» интервьюера верно. Вложенная 9X_jre синхронизация бесполезна, и в лучшем случае 9X_javax оптимизатор JVM сможет ее устранить.

Однако 9X_javax ни одно из решений не рекомендуется. Тут 9X_threads гораздо более глубокая проблема. Во-первых, мы 9X_multithreading должны сосредоточиться на правильности, прежде чем обсуждать 9X_threads производительность.

Судя по всему, getFileFromLocalFS(url) должен 9X_threading найти локальную копию, если она существует, а 9X_concurrent-programming не создать ее. Это было бы бессмысленно, если 9X_openjdk бы никто никогда не создавал такие копии, и 9X_jre весь вопрос о перекрывающихся попытках кэширования 9X_threads был бы спорным, если этот метод не выполняет 9X_concurrent-programming попытку кэширования. Таким образом, единственным 9X_openjdk другим задействованным методом, getFileFromWeb(url), должен 9X_javax быть тот, который создает локальную копию.

Это 9X_synchronized означает, что между этими двумя методами 9X_jdk существует скрытый протокол. getFileFromLocalFS(url) каким-то образом знает, как 9X_openjdk получить кешированный файл, созданный getFileFromWeb(url).

Это 9X_threading вызывает вопросы, например, что произойдет, если 9X_concurrency один поток находится в середине getFileFromWeb(url) создания 9X_threading кэшированной версии, а другой поток вызывает 9X_synchronized getFileFromLocalFS(url) для того же URL-адреса? Возвращает ли он 9X_concurency входной поток в еще незавершенный локальный 9X_threads файл? Есть две возможности:

  1. Этот вопрос как-то 9X_concurency решился. Это будет означать, что за кулисами 9X_jdk уже существует некоторая поточно-безопасная 9X_concurrent конструкция для предотвращения такого состояния 9X_javax гонки, следовательно, она должна использоваться 9X_concurrent-programming getFileFromWeb(url) для решения проблемы множественных попыток 9X_multithreading кэширования в первую очередь.

  2. Или эта проблема 9X_synchronized не была решена и отправной точкой был сломанный метод, поэтому 9X_jre в первую очередь нужно исправить это, а 9X_threads не пытаться улучшить производительность. Если 9X_java мы действительно попытаемся исправить это 9X_threading вне двух методов, т.е. в getFile, правильным будет 9X_jdk только второй вариант, вызывающий оба метода 9X_threads внутри synchronized.

В любом случае метод getFile не подходит 9X_openjdk для решения проблемы. Проблемы параллелизма 9X_concurrent должны решаться с помощью скрытого протокола, который уже 9X_concurrent-programming существует между двумя методами.

Например:

  • Если 9X_multithread эти два метода используют сопоставление 9X_java URL-адреса с локальным файлом, они должны 9X_threads использовать concurrent map и его атомарные операции обновления 9X_multithread для решения проблем параллелизма.

  • Если эти 9X_concurency два метода используют схему сопоставления 9X_jre для преобразования URL-адреса в локальный 9X_threads путь, где должна быть копия, а метод getFileFromLocalFS(url) просто 9X_concurrent-programming проверяет наличие файла, метод getFileFromWeb(url) должен создать 9X_jre файл с опцией CREATE_NEW для атомарной проверки наличия 9X_concurency и создания, если нет, вместе с блокировкой 9X_openjdk файлов для предотвращения чтения другими 9X_concurrency потоками, пока кэширование все еще выполняется.

11
0

Ответ #2

Ответ на вопрос: Может ли синхронизация внутреннего блока улучшить производительность уже синхронизированного метода?

Оба они в основном (несколько затененные) версии 9X_concurrent double-checked locking pattern. Но, учитывая, что вызов getFileFromLocalFS(url) сам по себе может 9X_multithread быть дорогостоящим (это вызов ввода-вывода, который 9X_synchronized всегда более затратен, чем операции в памяти), эта 9X_java форма шаблона на самом деле не идеальна. В 9X_threads идеале реализация блокировки с двойной проверкой 9X_threading должна быть максимально "плотной", то 9X_concurrent-programming есть две блокировки должны быть как можно 9X_concurrency ближе друг к другу.

Дополнительная синхронизация 9X_openjdk в #3 (предложение интервьюера) создает дополнительные 9X_threading накладные расходы и, что более важно, потенциально 9X_thread блокирует, когда в этом нет необходимости. "Производительность" — это 9X_concurrency широкий термин, который может означать разные 9X_threading вещи для разных людей и в разных контекстах; но 9X_multithread эта дополнительная синхронизация может (вероятно?) повлиять 9X_jdk на наблюдаемую пропускную способность в зависимости от обстоятельств.

1
0