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

Я читал многопоточность в Java, и я сталкивался с этим

Локальные переменные являются потокобезопасными в Java.

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

Может кто-нибудь, пожалуйста, дайте мне знать.

8 ответов

Решение

Когда вы создаете поток, у него будет свой собственный стек. Два потока будут иметь два стека, и один поток никогда не поделится своим стеком с другим потоком.

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

На youtube есть отличная лекция профессора Стэнфорда, которая может помочь вам понять эту концепцию.

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

public void someMethod(){

   long threadSafeInt = 0;

   threadSafeInt++;
}

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

Думайте о методах как об определениях функциональности. Когда два потока запускают один и тот же метод, они никоим образом не связаны между собой. Каждый из них будет создавать свою собственную версию каждой локальной переменной и не сможет каким-либо образом взаимодействовать друг с другом.

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

Рассмотрим эти два случая:

public class NotThreadsafe {
    int x = 0;
    public int incrementX() {
        x++;
        return x;
    }
}

public class Threadsafe {
    public int getTwoTimesTwo() {
        int x = 1;
        x++;
        return x*x;
    }
}

В первом два потока работают на одном экземпляре NotThreadsafe увидим тот же х. Это может быть опасно, потому что потоки пытаются изменить х! Во втором два потока работают на одном экземпляре Threadsafe будет видеть совершенно разные переменные, и не может влиять друг на друга.

В дополнение к другим ответам, таким как Nambari.

Я хотел бы отметить, что вы можете использовать локальную переменную в методе с аномальным типом:

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

Считайте этот незаконный код:

public void nonCompilableMethod() {
    int i=0;
    for(int t=0; t<100; t++)
    {
      new Thread(new Runnable() {
                    public void run() {
                      i++; //compile error, i must be final:
                      //Cannot refer to a non-final variable i inside an
                      //inner class defined in a different method
                    }
       }).start();
     }
  }

Если бы java это разрешил (как C# делает через "замыкания"), локальная переменная больше не будет поточно-ориентированной во всех обстоятельствах. В этом случае значение i в конце всех потоков не гарантируется 100,

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

Однако внимательно следите за тем, что именно подразумевается под этим: только записи в саму переменную являются потокобезопасными; вызов методов для объекта, на который он ссылается, не является поточно-ориентированным. То же самое касается прямого обновления переменных объекта.

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

В основном в java есть четыре типа хранилищ для хранения информации о классе и данных:

Метод Область, Куча,JAVA Stack, ПК

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

Каждый метод в Java является стековым фреймом. таким образом, когда один метод вызывается потоком, этот кадр стека загружается в его стек JAVA. Все локальные переменные, которые находятся в этом кадре стека, и связанный стек операндов не используются совместно другими. ПК будет иметь информацию о следующей инструкции для выполнения в байтовом коде метода. так что все локальные переменные являются БЕЗОПАСНЫМИ.

@Weston также дал хороший ответ.

Мне нравится объяснение Дженкова

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

Все локальные переменные примитивных типов (логические, байтовые, короткие, char, int, long, float, double) полностью хранятся в стеке потоков и, следовательно, не видны другим потокам. Один поток может передать копию примитивной переменной другому потоку, но он не может совместно использовать примитивную локальную переменную.

Куча содержит все объекты, созданные в вашем приложении Java, независимо от того, какой поток создал объект. Это включает в себя версии объектов примитивных типов (например, Byte, Integer, Long и т. Д.). Не имеет значения, был ли объект создан и назначен локальной переменной или создан как переменная-член другого объекта, этот объект все еще сохраняется в куче.

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

Локальная переменная также может быть ссылкой на объект. В этом случае ссылка (локальная переменная) сохраняется в стеке потоков, но сам объект, если он хранится в куче.

Пожалуйста, прочитайте больше - http://tutorials.jenkov.com/java-concurrency/java-memory-model.html

В стеке потока хранятся только локальные переменные.

Локальная переменная, которая естьprimitive type (например, int, long...) хранится в thread stack и как следствие - другой поток не имеет к нему доступа.

Локальная переменная, которая естьreference type (преемник Object) состоит из 2 частей - адреса (который хранится на thread stack) и объект (который хранится на heap)


class MyRunnable implements Runnable() {
    public void run() {
        method1();
    }

    void method1() {
        int intPrimitive = 1;

        method2();
    }

    void method2() {
        MyObject1 myObject1 = new MyObject1();
    }
}

class MyObject1 {
    MyObject2 myObject2 = new MyObject2();
}

class MyObject2 {
    MyObject3 myObject3 = MyObject3.shared;
}

class MyObject3 {
    static MyObject3 shared = new MyObject3();

    boolean b = false;
}

Другие вопросы по тегам