11

Что значит 'synchronized'?

24

Я столкнулся с несколькими вопросами относительно использования и значимости ключевого слова synchronized в Java.

  • Каково значение ключевого слова synchronized?
  • Когда следует использовать synchronized для методов?
  • Что это означает как с программной, так и с логической точки зрения?

5 ответ(ов)

3

Думаю, мы уже достаточно обсудили теоретические аспекты, поэтому давайте рассмотрим этот код:

public class TestThread extends Thread {
    String name;
    TheDemo theDemo;

    public TestThread(String name, TheDemo theDemo) {
        this.theDemo = theDemo;
        this.name = name;
        start();
    }

    @Override
    public void run() {
        theDemo.test(name);
    }

    public static class TheDemo {
        public synchronized void test(String name) {
            for (int i = 0; i < 10; i++) {
                System.out.println(name + " :: " + i);
                try {
                    Thread.sleep(500);
                } catch (Exception e) {
                    System.out.println(e.getMessage());
                }
            }
        }
    }

    public static void main(String[] args) {
        TheDemo theDemo = new TheDemo();
        new TestThread("THREAD 1", theDemo);
        new TestThread("THREAD 2", theDemo);
        new TestThread("THREAD 3", theDemo);
    }
}

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

Когда поток вызывает синхронизированный метод test объекта (в нашем случае это экземпляр класса TheDemo), он получает блокировку этого объекта, и новый поток не сможет вызвать НИКАКОЙ синхронизированный метод данного объекта, пока предыдущий поток, который получил блокировку, не освободит её.

То же самое происходит, когда вызывается любой статический синхронизированный метод класса. Поток захватывает блокировку, связанную с классом (в данном случае любой нестатический синхронизированный метод экземпляра этого класса могут вызывать любые потоки, потому что блокировка на уровне объекта всё еще доступна). Любой другой поток не сможет вызвать статический синхронизированный метод класса до тех пор, пока блокировка на уровне класса не будет освобождена потоком, который её держит.

Вывод с использованием synchronized:

THREAD 1 :: 0
THREAD 1 :: 1
THREAD 1 :: 2
THREAD 1 :: 3
THREAD 1 :: 4
THREAD 1 :: 5
THREAD 1 :: 6
THREAD 1 :: 7
THREAD 1 :: 8
THREAD 1 :: 9
THREAD 3 :: 0
THREAD 3 :: 1
THREAD 3 :: 2
THREAD 3 :: 3
THREAD 3 :: 4
THREAD 3 :: 5
THREAD 3 :: 6
THREAD 3 :: 7
THREAD 3 :: 8
THREAD 3 :: 9
THREAD 2 :: 0
THREAD 2 :: 1
THREAD 2 :: 2
THREAD 2 :: 3
THREAD 2 :: 4
THREAD 2 :: 5
THREAD 2 :: 6
THREAD 2 :: 7
THREAD 2 :: 8
THREAD 2 :: 9

Вывод без использования synchronized:

THREAD 1 :: 0
THREAD 2 :: 0
THREAD 3 :: 0
THREAD 1 :: 1
THREAD 2 :: 1
THREAD 3 :: 1
THREAD 1 :: 2
THREAD 2 :: 2
THREAD 3 :: 2
THREAD 1 :: 3
THREAD 2 :: 3
THREAD 3 :: 3
THREAD 1 :: 4
THREAD 2 :: 4
THREAD 3 :: 4
THREAD 1 :: 5
THREAD 2 :: 5
THREAD 3 :: 5
THREAD 1 :: 6
THREAD 2 :: 6
THREAD 3 :: 6
THREAD 1 :: 7
THREAD 2 :: 7
THREAD 3 :: 7
THREAD 1 :: 8
THREAD 2 :: 8
THREAD 3 :: 8
THREAD 1 :: 9
THREAD 2 :: 9
THREAD 3 :: 9

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

1

Ключевое слово synchronized предотвращает одновременный доступ нескольких потоков к блоку кода или объекту. Все методы Hashtable являются synchronized, поэтому в любой момент времени только один поток может выполнять любой из них.

При использовании нон-synchronized конструкций, таких как HashMap, вам необходимо самостоятельно реализовать механизмы потокобезопасности в вашем коде, чтобы избежать ошибок согласованности.

0

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

Второй поток будет ждать завершения выполнения первого потока. Издержки заключаются в снижении скорости, однако преимущество заключается в гарантированной согласованности данных.

Если же ваше приложение однопоточное, synchronized блоки не приносят пользы.

0

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

Это часто называют обеспечением потокобезопасности класса, но я бы сказал, что это эвфемизм. Хотя верно, что синхронизация защищает внутреннее состояние класса Vector от порчи, это обычно не очень помогает пользователям Vector.

Рассмотрим следующий пример:

if (vector.isEmpty()) {
    vector.add(data);
}

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

Таким образом, вам также придется синхронизировать код вашего приложения.

Поскольку синхронизация на уровне метода a) является дорогой стоимостью, когда она не нужна, и b) недостаточной, когда синхронизация необходима, сейчас существуют несинхронизированные замены (например, ArrayList вместо Vector).

В последнее время был выпущен пакет java.util.concurrent с несколькими умными утилитами, которые решают проблемы многопоточности.

0

Обзор

Ключевое слово synchronized в Java связано с безопасностью потоков, то есть когда несколько потоков читают или записывают одну и ту же переменную.

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

Ключевое слово synchronized используется для определения блока кода, в котором несколько потоков могут безопасно обращаться к одной и той же переменной.

Углубление

Синтаксически ключевое слово synchronized принимает параметр Object (называемый объектом блокировки), за которым следует { блок кода }.

  • Когда выполнение встречает это ключевое слово, текущий поток пытается "заблокировать/получить/владеть" (выберите вариант) объектом блокировки и выполнить связанный блок кода после того, как блокировка была получена.
  • Любые записи в переменные внутри синхронизированного блока кода гарантированно будут видны каждому другому потоку, который также выполняет код внутри синхронизированного блока, используя тот же объект блокировки.
  • Только один поток может держать блокировку в любое время, в течение которого все остальные потоки, пытающиеся получить тот же объект блокировки, будут ждать (приостановят свое выполнение). Блокировка будет освобождена, когда выполнение выйдет из синхронизированного блока кода.

Синхронизированные методы:

Добавление ключевого слова synchronized к определению метода эквивалентно тому, что всё тело метода обёрнуто в синхронизированный блок кода, при этом объект блокировки равен this (для экземплярных методов) и ClassInQuestion.getClass() (для методов класса).

- Экземплярный метод — это метод, который не имеет ключевого слова static.

- Метод класса — это метод, который имеет ключевое слово static.

Техническо

Без синхронизации нет гарантии, в каком порядке произойдут чтения и записи, что может привести к тому, что переменная окажется в состоянии "мусора".

(Например, переменная может быть записана с половиной битов, записанных одним потоком, и другой половиной — другим потоком, оставив переменную в состоянии, которое ни один из потоков не пытался записать, а в комбинированном беспорядке.)

Недостаточно завершить операцию записи в одном потоке до (по реальному времени) того, как другой поток её прочитает, так как аппаратное обеспечение могло закэшировать значение переменной, и поток чтения увидит закэшированное значение вместо того, что было записано.

Заключение

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

Другими словами: используйте синхронизацию, атомарные операции или классы, которые используют их за кулисами.

Источники

http://docs.oracle.com/javase/specs/jls/se8/html/index.html

Java® Language Specification, 2015-02-13

Чтобы ответить на вопрос, пожалуйста, войдите или зарегистрируйтесь