Dlaczego zmienne lokalne są bezpieczne wątkowo w Javie

91

Czytałem wielowątkowość w Javie i natknąłem się na to

Zmienne lokalne są bezpieczne dla wątków w Javie.

Od tego czasu zastanawiałem się, jak / dlaczego zmienne lokalne są bezpieczne dla wątków.

Czy ktoś może mi dać znać.

Anand
źródło
27
Ponieważ są przydzielane w stosie. A wątki nie dzielą się stosem… jego unikalne dla każdego…
Rohit Jain

Odpowiedzi:

103

Kiedy tworzysz wątek, będzie miał utworzony własny stos. Dwa wątki będą miały dwa stosy, a jeden wątek nigdy nie będzie dzielił stosu z innym wątkiem.

Wszystkie zmienne lokalne zdefiniowane w twoim programie zostaną przydzielone w pamięci na stosie (jak skomentował Jatin, pamięć tutaj oznacza wartość odniesienia dla obiektów i wartość dla typów pierwotnych) (Każda metoda wywoływana przez wątek tworzy ramkę stosu na swoim własnym stosie). Jak tylko wykonanie metody zostanie zakończone przez ten wątek, ramka stosu zostanie usunięta.

Na youtube jest świetny wykład profesora Stanforda, który może pomóc w zrozumieniu tej koncepcji.

kosa
źródło
13
Przepraszam, mylisz się, tylko prymitywne zmienne lokalne są przechowywane na stosie. Pozostałe zmienne są przechowywane w Heap. Java 7 wprowadziła analizę ucieczki, która dla niektórych zmiennych może alokować ją na stosie
Jatin,
6
Stos zawiera tylko odniesienie do obiektu na stercie. Ponieważ stos zostaje wyczyszczony, to samo dotyczy odwołania. stąd jest dostępny do wywozu śmieci
Jatin
6
@Jatin: Masz rację. Kiedy miałem na myśli pamięć, mam na myśli wartość referencyjną dla obiektów i wartości dla prymitywów (myślę, że początkujący programiści również wiedzą, że obiekty są na stercie).
kosa
2
@Nambari, ale jeśli wartość odniesienia wskazuje na wspólną zmienną. Jak więc możemy powiedzieć, że jest bezpieczny dla wątków?
H.Rabiee,
3
@hajder: Co sprawia, że ​​zmienna jest udostępniana? zacznij od tego. Zmienne instancji lub klasy, prawda? nie zmienne lokalne ORAZ przeczytaj odpowiedź Marko Toplink w tym wątku, myślę, że jest to punkt, w którym jesteś zdezorientowany.
kosa
19

Zmienne lokalne są przechowywane we własnym stosie każdego wątku. Oznacza to, że zmienne lokalne nigdy nie są współużytkowane między wątkami. Oznacza to również, że wszystkie lokalne zmienne pierwotne są bezpieczne dla wątków.

public void someMethod(){

   long threadSafeInt = 0;

   threadSafeInt++;
}

Lokalne odniesienia do obiektów są nieco inne. Samo odniesienie nie jest udostępniane. Jednak obiekt, do którego się odwołuje, nie jest przechowywany na stosie lokalnym każdego wątku. Wszystkie obiekty są przechowywane w udostępnionej stercie. Jeśli obiekt utworzony lokalnie nigdy nie ucieknie z metody, w której został utworzony, jest bezpieczny wątkowo. W rzeczywistości możesz również przekazać go innym metodom i obiektom, o ile żadna z tych metod lub obiektów nie udostępnia przekazanego obiektu innym wątkom

Renjith
źródło
W agrumentie jest błąd, spójrz na komentarze @Nambari
Jatin
Jeśli wskazujesz na fakt, że localSafeInt zawsze będzie wynosić 0, a następnie 1, a następnie zostanie usunięty i tak, to dobrze. Pokazuje więc, że ta zmienna nie jest dzielona między wątkami, a tym samym nie ma na nią wpływu wielowątkowość. Myślę, że możesz trochę bardziej wskazać, że wątki bezpieczne to zawsze tylko 0 lub 1
tObi
14

Pomyśl o metodach, takich jak definicje funkcjonalności. Gdy dwa wątki uruchamiają tę samą metodę, nie są w żaden sposób powiązane. Każdy z nich utworzy własną wersję każdej zmiennej lokalnej i nie będzie w stanie w żaden sposób współdziałać ze sobą.

Jeśli zmienne nie są lokalne (jak zmienne instancji zdefiniowane poza metodą na poziomie klasy), to są one dołączane do instancji (a nie do pojedynczego uruchomienia metody). W takim przypadku dwa wątki działające tą samą metodą widzą jedną zmienną i nie jest to bezpieczne dla wątków.

Rozważ te dwa przypadki:

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;
    }
}

W pierwszym dwa wątki działające w tej samej instancji programu NotThreadsafezobaczą ten sam x. Może to być niebezpieczne, ponieważ wątki próbują zmienić x! W drugim przypadku dwa wątki działające w tej samej instancji programu Threadsafebędą widzieć zupełnie różne zmienne i nie będą mogły na siebie wpływać.

Cory Kendall
źródło
6

Każde wywołanie metody ma swoje własne zmienne lokalne i oczywiście wywołanie metody ma miejsce w pojedynczym wątku. Zmienna, która jest aktualizowana tylko przez jeden wątek, jest z natury bezpieczna dla wątków.

Jednak uważnie obserwuj, co dokładnie to oznacza: tylko zapisy do samej zmiennej są bezpieczne dla wątków; wywoływanie metod na obiekcie, do którego się odwołuje, nie jest z natury bezpieczne dla wątków . To samo dotyczy bezpośredniego aktualizowania zmiennych obiektu.

Marko Topolnik
źródło
1
Mówisz, że „wywoływanie metod na obiekcie, do którego się odnosi, nie jest z natury bezpieczne dla wątków”. Ale w jaki sposób obiekt, do którego odwołuje się odwołanie lokalne metody - wystąpienie w tym zakresie metody - może być współużytkowany przez dwa wątki? Czy mógłbyś wskazać na przykładzie?
Akshay Lokur,
1
Zmienna lokalna może, ale nie musi, zawierać obiekt utworzony w ramach zakresu metody, który nie był częścią pytania. Nawet jeśli tak jest, metoda może uzyskać dostęp do stanu udostępnionego.
Marko Topolnik
6

Oprócz innych odpowiedzi, takich jak Nambari.

Chciałbym zaznaczyć, że możesz użyć zmiennej lokalnej w metodzie anonimowej:

Ta metoda może być wywoływana w innych wątkach, które mogą naruszyć bezpieczeństwo wątków, więc java wymusza zadeklarowanie wszystkich zmiennych lokalnych używanych w anonimowych typach jako ostateczne.

Rozważ ten nielegalny kod:

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();
     }
  }

Gdyby Java na to pozwalała (tak jak C # robi to przez „zamknięcia”), zmienna lokalna nie byłaby już wątkowo bezpieczna w każdych okolicznościach. W takim przypadku wartość ina końcu wszystkich wątków nie jest gwarantowana 100.

Weston
źródło
Cześć Weston, Z powyższej dyskusji i poniższych odpowiedzi zrozumiałem, że java zapewnia bezpieczeństwo wątków dla wszystkich zmiennych lokalnych. Czy mogę wiedzieć, jakie jest rzeczywiste użycie słowa kluczowego zsynchronizowanego? czy mógłbyś wyjaśnić na przykładzie takim jak ten.
Prabhu
5

Wątek będzie miał własny stos. Dwa wątki będą miały dwa stosy, a jeden wątek nigdy nie będzie dzielił stosu z innym wątkiem. Zmienne lokalne są przechowywane we własnym stosie każdego wątku. Oznacza to, że zmienne lokalne nigdy nie są współużytkowane między wątkami.

Sudhirkumar Murkute
źródło
3

Zasadniczo istnieją cztery typy pamięci masowej w java do przechowywania informacji o klasach i danych:

Obszar metod, sterta, stos JAVA, PC

więc obszar Metody i Sterta są współdzielone przez wszystkie wątki, ale każdy wątek ma własny stos JAVA i komputer, który nie jest współdzielony przez żadne inne wątki.

Każda metoda w java jest jak ramka stosu. tak więc, gdy jedna metoda jest wywoływana przez wątek, ramka stosu jest ładowana do stosu JAVA. Wszystkie zmienne lokalne, które są w tej ramce stosu i stosie powiązanych operandów, nie są współdzielone przez inne. Komputer będzie miał informację o następnej instrukcji do wykonania w kodzie bajtowym metody. więc wszystkie zmienne lokalne są BEZPIECZNE.

@Weston również dał dobrą odpowiedź.

Prashant
źródło
1

Na stosie wątków są przechowywane tylko zmienne lokalne .

Zmienna lokalna czyli primitive type(np. Int, long ...) jest przechowywana w tym thread stackwątku i w rezultacie inny wątek nie ma do niej dostępu.

Zmienna lokalna, która jest reference type(następcą Object) zawiera z 2 części - adres (na którym jest przechowywany thread stack) i obiekt (na którym jest przechowywany 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;
}

wprowadź opis obrazu tutaj

yoAlex5
źródło