Рубрики
Без рубрики

Почему локальные переменные потокобезопасны в Java

Узнайте, почему локальные переменные потокобезопасны в Java.

Автор оригинала: Martin van Wingerden.

1. введение

До того, как мы ввели потокобезопасность, и как ее можно достичь .

В этой статье мы рассмотрим локальные переменные и объясним, почему они потокобезопасны.

2. Память стека и потоки

Давайте начнем с краткого обзора модели памяти JVM.

Самое главное, что JVM разбивает доступную память на стек и память кучи . Во-первых, он хранит все объекты в куче. Во-вторых, он хранит локальные примитивы и ссылки на локальные объекты в стеке .

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

3. Пример

Давайте теперь продолжим с небольшим примером кода, содержащим локальный примитив и (примитивное) поле:

public class LocalVariables implements Runnable {
    private int field;

    public static void main(String... args) {
        LocalVariables target = new LocalVariables();
        new Thread(target).start();
        new Thread(target).start();
    }

    @Override
    public void run() {
        field = new SecureRandom().nextInt();
        int local = new SecureRandom().nextInt();
        System.out.println(field + ":" + local);
    }
}

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

Внутри метода run мы обновляем поле field класса Local Variables|/. Во-вторых, мы видим назначение локальному примитиву. Наконец, мы выводим эти два поля на консоль.

Давайте взглянем на расположение в памяти всех полей.

Во-первых, поле | является полем класса Локальных переменных . Поэтому он живет на куче. Во-вторых, локальная переменная number является примитивом. Следовательно, он находится в стеке.

Оператор println – это место, где все может пойти не так при запуске двух потоков.

Во-первых, поле field имеет высокую вероятность возникновения проблем, поскольку и ссылка, и объект находятся в куче и совместно используются нашими потоками. Примитив local будет в порядке, так как значение находится в стеке. Следовательно, JVM не разделяет local между потоками.

Поэтому при выполнении мы могли бы, например, получить следующий результат:

 821695124:1189444795
821695124:47842893

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

4. Локальные Переменные Внутри Лямбд

Лямбды (и анонимные внутренние классы ) могут быть объявлены внутри метода и могут обращаться к локальным переменным метода. Однако без каких-либо дополнительных охранников это может привести к большим неприятностям.

До JDK 8 существовало явное правило, согласно которому анонимные внутренние классы могли обращаться только к конечным локальным переменным . JDK 8 ввел новую концепцию эффективного финала, и правила были сделаны менее строгими. Мы уже сравнивали final и effectively final раньше, а также подробнее обсуждали effectively final при использовании лямбд .

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

Мы можем увидеть это поведение на практике в следующем примере:

public static void main(String... args) {
    String text = "";
    // text = "675";
    new Thread(() -> System.out.println(text))
            .start();
}

В этом случае раскомментирование кода в строке 3 приведет к ошибке компиляции. Потому что тогда локальная переменная text больше не является фактически окончательной.

5. Заключение

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

Как всегда, полный исходный код статьи доступен на GitHub .