Подтвердить что ты не робот

Какова цель использования синхронизированного (Thread.currentThread()) {...} в Java?

В нашем проекте я столкнулся со следующим кодом:

synchronized (Thread.currentThread()){
    //some code
}

Я не понимаю причину использования синхронизации на currentThread.

Есть ли разница между

synchronized (Thread.currentThread()){
    //some code
}

и просто

//some code

Можете ли вы привести пример, который показывает разницу?

UPDATE

более подробно этот код выглядит следующим образом:

synchronized (Thread.currentThread()) {
       Thread.currentThread().wait(timeInterval);
}

Похоже, просто Thread.sleep(timeInterval). Это правда?

4b9b3361

Ответ 1

рассмотрим это

    Thread t = new Thread() {
        public void run() { // A
            synchronized (Thread.currentThread()) {
                System.out.println("A");
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                }
            }
        }
    };
    t.start();
    synchronized (t) { // B
        System.out.println("B");
        Thread.sleep(5000);
    }

блоки A и B не могут работать одновременно, поэтому в данном тесте вывод "A" или "B" будет задержан на 5 секунд, первый из которых будет первым: undefined

Ответ 2

Хотя это почти определенно антипаттерн и нужно решать по-другому, ваш непосредственный вопрос по-прежнему требует ответа. Если вся ваша кодовая база никогда не получает блокировку для любого экземпляра Thread, отличного от Thread.currentThread(), тогда эта блокировка никогда не будет решена. Однако, если в другом месте у вас есть

synchronized (someSpecificThreadInstance) { ... }

тогда такой блок должен будет бороться с вашим показанным блоком для той же блокировки. Может случиться так, что поток, достигающий synchronized (Thread.currentThread()), должен ждать, пока какой-либо другой поток откажется от блокировки.

Ответ 3

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

В блоках synchronized есть интересный побочный эффект, вызывающий создание барьера памяти во время выполнения перед входом и после выхода из блока. Предел памяти - это специальная инструкция для ЦП, которая принудительно использует все переменные, которые распределяются между несколькими потоками, чтобы вернуть их последние значения. Обычно поток работает со своей собственной копией общей переменной, и ее значение видимо только для этого потока. Предел памяти указывает, что поток обновляет значение таким образом, чтобы изменение было видимым для других потоков.

Таким образом, синхронизированный блок в этом случае не выполняет блокировку (так как не будет реального случая блокировки и ожидания, так как я не могу думать об этом) (кроме случаев, упомянутый в этот ответ), но вместо этого он применяет значения общих полей для возврата их последнего значения. Это, однако, верно, если в других местах кода, которые работают с рассматриваемыми переменными , также используются барьеры памяти (например, с тем же блоком synchronized вокруг операций обновления/переназначения). Тем не менее, это не решение, чтобы избежать условий гонки.

Если вам интересно, я рекомендую вам прочитать статью . Речь идет о барьерах памяти и блокировке в С# и .NET Framework, но проблема аналогична для Java и JVM (за исключением поведения изменчивых полей). Это помогло мне понять, как работают нити, изменчивые поля и блокировки.

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

  • Предел памяти не подразумевает блокировку. Доступ по-прежнему будет несинхронизирован и будет зависеть от условий гонки и других потенциальных проблем, с которыми можно столкнуться. Единственным преимуществом является то, что поток может считывать последние значения полей разделяемой памяти без использования блокировок. Некоторые методы используют аналогичные подходы, если рабочий поток читает только значения, и он заботится только о том, чтобы они были самыми настоящими, избегая при этом накладных расходов на блокировки - прецедентом может быть высокопроизводительный алгоритм одновременной обработки данных.
  • Подход выше ненадежный. Согласно комментарию Хольгера, компилятор может устранить инструкции блокировки при оптимизации, поскольку это может считать их ненужными. Это также устранит барьеры памяти. Тогда код не будет блокировать, и он не будет работать так, как ожидалось, если бы блокировка предназначалась для использования или целью было создание барьера памяти.
  • Приведенный выше подход также ненадежен, поскольку JVM времени выполнения может удалить синхронизацию, когда он может доказать, что монитор никогда не будет получен другим потоком, что верно для этой конструкции, если код никогда не синхронизируется с другим объектом потока, который не является текущим объект потока нити. Таким образом, даже если он работает во время тестирования в системе A, он может выйти из строя под другой JVM в системе B. Еще хуже то, что код может работать некоторое время, а затем прекратить работу при применении оптимизаций.
  • В настоящее время намерения кода остаются неизменными, поэтому следует использовать более явные и выразительные средства для достижения его эффекта (см. комментарий Марко Топольника для справки).

Ответ 4

Вы реализуете рекурсивный мьютекс.

то есть. тот же поток может войти в блок синхронизации, но не в другие потоки.