Чому локальні змінні безпечні для потоку в Java


91

Я читав багатопоточність на Java, і я стикався з цим

Локальні змінні безпечні для потоку в Java.

З тих пір я думав, як / чому місцеві змінні безпечні для потоку.

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


27
Тому що вони виділені в Stack. І нитки не діляться стеком .. свій унікальний для кожного ..
Рохіт Джейн

Відповіді:


103

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

Усім локальним змінним, визначеним у вашій програмі, буде виділено пам'ять у стеку (як зауважив Ятін, тут пам'ять означає, посилальне значення для об'єктів та значення для примітивних типів) (Кожен виклик методу потоком створює фрейм стека у власному стеку). Як тільки виконання методу завершиться цим потоком, кадр стека буде видалений.

На YouTube є чудова лекція професора Стенфорда, яка може допомогти вам зрозуміти цю концепцію.


13
Вибачте, ви помиляєтесь, у стеку зберігаються лише примітивні локальні змінні. Решта всі змінні зберігаються в Heap. Java 7 представила аналіз втечі, який для деяких змінних може виділити його в стек
Ятін,

6
Стек лише містить посилання на об’єкт у купі. Оскільки стек очищається, то і посилання. отже, він доступний для збору сміття
Ятін,

6
@Jatin: Ви праві. Коли я мав на увазі пам'ять, я маю на увазі посилальне значення для об'єктів і значення для примітивів (я думаю, що розробники-початківці також знають, що Об'єкти знаходяться в купі).
kosa

2
@Nambari, але якщо посилальне значення вказує на спільну змінну. Тоді як ми можемо сказати, що це безпечно для ниток?
H.Rabiee

3
@hajder: Що робить змінну спільною? Почніть звідти. Або екземпляр, або змінні класу, правда? а не локальні змінні І ПРОЧИТАЙТЕ відповідь Марко Топлінка в цій темі, я думаю, що це те, що вас бентежить.
kosa

19

Локальні змінні зберігаються у власному стеку кожного потоку. Це означає, що локальні змінні ніколи не діляться між потоками. Це також означає, що всі локальні примітивні змінні безпечні для потоку.

public void someMethod(){

   long threadSafeInt = 0;

   threadSafeInt++;
}

Місцеві посилання на об’єкти дещо відрізняються. Само посилання не є спільним. Однак об’єкт, на який посилається, не зберігається в локальному стеку кожного потоку. Всі об'єкти зберігаються у спільній купі. Якщо об’єкт, створений локально, ніколи не уникає методу, в якому він був створений, це безпечно для потоку. Насправді ви також можете передавати його іншим методам та об'єктам, якщо жоден з цих методів або об'єктів не робить переданий об'єкт доступним для інших потоків


Існує помилка в agrument, пл подивитися на коментарі відповіді @Nambari
Jatin

Якщо ви вказуєте на той факт, що localSafeInt завжди буде просто 0, то 1, а потім все одно видаляється, це добре. Отже, це показує, що ця змінна не є спільною для потоків і, отже, не впливає на багатопоточність .. я думаю, ви могли б ще трохи зазначити, що threadsafe завжди просто 0 або 1
tObi

14

Подумайте про такі методи, як визначення функціональності. Коли два потоки запускають один і той же метод, вони жодним чином не пов’язані. Вони кожен створять свою власну версію кожної локальної змінної і не зможуть взаємодіяти між собою жодним чином.

Якщо змінні не є локальними (як змінні екземпляра, визначені за межами методу на рівні класу), тоді вони приєднуються до екземпляра (не до одного запуску методу). У цьому випадку два потоки, що виконують один і той же метод, бачать одну змінну, і це не є безпечним для потоків.

Розглянемо ці два випадки:

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, побачать однаковий х. Це може бути небезпечно, оскільки нитки намагаються змінити x! У другому, два потоки, що працюють на одному екземплярі Threadsafe, бачитимуть абсолютно різні змінні і не можуть впливати один на одного.


6

Кожне виклик методу має свої локальні змінні, і, очевидно, виклик методу відбувається в одному потоці. Змінна, яка оновлюється лише одним потоком, за своєю суттю є безпечною для потоків.

Однак уважно стежте за тим, що саме мається на увазі під цим: лише записи в саму змінну безпечні для потоків; виклик методів для об'єкта, на який він посилається, за своєю суттю не є безпечним для потоків . Те саме стосується і безпосереднього оновлення змінних об’єкта.


1
Ви говорите, що "виклик методів для об'єкта, на який він посилається, за своєю суттю не є безпечним для потоків". Але як об’єкт, на який посилається локальне посилання методу, - інстанційоване в цій області методу - може бути спільним для двох потоків? Не могли б ви вказати на своєму прикладі?
Акшай Локур

1
Локальна змінна може містити, а може і не містити об’єкт, інстанційований у межах методу, що не було частиною питання. Навіть якщо це так, метод може отримати доступ до спільного стану.
Марко Топольник

6

На додаток до інших відповідей, таких як Намбарі.

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

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

Розглянемо цей незаконний код:

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 забезпечує безпеку потоків для всіх локальних змінних. Чи можу я знати, що тоді фактично використовується синхронізоване ключове слово? не могли б ви пояснити на прикладі, подібному до цього.
Прабху,

5

Потік матиме власний стек. Два потоки матимуть два стеки, і один потік ніколи не ділиться своїм стеком з іншим потоком. Локальні змінні зберігаються у власному стеку кожного потоку. Це означає, що локальні змінні ніколи не діляться між потоками.


3

В основному в Java є чотири типи сховищ для зберігання інформації та даних класу:

Область методів, купа, стек JAVA, ПК

отже, область методу та куча спільно використовуються усіма потоками, але кожен потік має свій власний стек JAVA та ПК, що не є спільним для інших потоків.

Кожен метод у Java є як фрейм стека. отже, коли один метод викликається потоком, фрейм стека завантажується в його стек JAVA. Усі локальні змінні, що знаходяться в цьому фреймі стека та пов'язаний стек операндів, не діляться іншими. ПК матиме інформацію про наступну інструкцію для виконання в байт-коді методу. тому всі локальні змінні БЕЗПЕЧНО.

@Weston також дав хорошу відповідь.


1

У стеці потоків зберігаються лише локальні змінні.

Локальна змінна, яка є 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;
}

введіть тут опис зображення

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.