Perché le variabili locali sono thread-safe in Java


90

Stavo leggendo il multi-threading in Java e mi sono imbattuto in questo

Le variabili locali sono thread-safe in Java.

Da allora ho pensato come / perché le variabili locali sono thread-safe.

Qualcuno può farmelo sapere.


27
Perché sono allocati in Stack. E i thread non condividono lo stack .. è unico per ciascuno ..
Rohit Jain

Risposte:


103

Quando crei un thread, verrà creato il proprio stack. Due thread avranno due stack e un thread non condivide mai lo stack con un altro thread.

Tutte le variabili locali definite nel programma verranno allocate in memoria nello stack (come ha commentato Jatin, memoria qui significa valore di riferimento per gli oggetti e valore per i tipi primitivi) (Ogni chiamata al metodo da parte di un thread crea uno stack frame sul proprio stack). Non appena l'esecuzione del metodo viene completata da questo thread, lo stack frame verrà rimosso.

C'è una grande lezione del professore di Stanford su YouTube che può aiutarti a comprendere questo concetto.


13
Mi dispiace, ti sbagli, solo le variabili locali primitive sono memorizzate nello stack. Resto tutte le variabili sono memorizzate su Heap. Java 7 ha introdotto l'analisi di fuga, che per alcune variabili potrebbe allocarla nello stack
Jatin

6
Stack contiene solo il riferimento all'oggetto nell'heap. Poiché lo stack viene cancellato, anche il riferimento. quindi è disponibile per la raccolta dei rifiuti
Jatin

6
@ Jatin: hai ragione. Quando intendevo memoria, intendo valore di riferimento per oggetti e valori per primitive (penso che anche gli sviluppatori inesperti sappiano che gli oggetti sono su heap).
kosa

2
@Nambari ma se il valore di riferimento punta a una variabile condivisa. Allora come possiamo dire che è thread-safe?
H.Rabiee

3
@hajder: cosa rende una variabile condivisa? inizia da lì. O variabili di istanza o di classe, giusto? non variabili locali E leggi la risposta di Marko Toplink in questo thread, penso che sia questo il punto su cui sei confuso.
kosa

19

Le variabili locali vengono memorizzate nello stack di ogni thread. Ciò significa che le variabili locali non vengono mai condivise tra i thread. Ciò significa anche che tutte le variabili primitive locali sono thread-safe.

public void someMethod(){

   long threadSafeInt = 0;

   threadSafeInt++;
}

I riferimenti locali agli oggetti sono leggermente diversi. Il riferimento stesso non è condiviso. L'oggetto a cui si fa riferimento, tuttavia, non viene archiviato nello stack locale di ogni thread. Tutti gli oggetti vengono archiviati nell'heap condiviso. Se un oggetto creato localmente non sfugge mai al metodo in cui è stato creato, è thread-safe. In effetti puoi anche passarlo ad altri metodi e oggetti purché nessuno di questi metodi o oggetti renda l'oggetto passato disponibile ad altri thread


C'è un errore nell'agricoltura, guarda i commenti della risposta di @Nambari
Jatin

Se stai indicando il fatto che localSafeInt sarà sempre solo 0, quindi 1 e quindi cancellato comunque, va bene. Quindi mostra che questa variabile non è condivisa tra i thread e quindi non è influenzata dal multi threading .. penso che potresti sottolineare un po 'di più che threadsafe è sempre solo 0 o 1
tObi

14

Pensa a metodi come definizioni di funzionalità. Quando due thread eseguono lo stesso metodo, non sono in alcun modo correlati. Ciascuno di loro creerà la propria versione di ciascuna variabile locale e non sarà in grado di interagire in alcun modo.

Se le variabili non sono locali (come le variabili di istanza definite al di fuori di un metodo a livello di classe), vengono associate all'istanza (non a una singola esecuzione del metodo). In questo caso, due thread che eseguono lo stesso metodo vedono entrambi l'unica variabile e questo non è thread-safe.

Considera questi due casi:

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

Nel primo, due thread in esecuzione sulla stessa istanza di NotThreadsafevedranno la stessa x. Questo potrebbe essere pericoloso, perché i thread stanno cercando di cambiare x! Nel secondo, due thread in esecuzione sulla stessa istanza di Threadsafevedranno variabili completamente diverse e non possono influenzarsi a vicenda.


6

Ogni chiamata di metodo ha le sue variabili locali e, ovviamente, una chiamata di metodo avviene in un singolo thread. Una variabile che viene aggiornata solo da un singolo thread è intrinsecamente thread-safe.

Tuttavia , tieni d'occhio cosa si intende esattamente con questo: solo le scritture sulla variabile stessa sono thread-safe; la chiamata di metodi sull'oggetto a cui fa riferimento non è intrinsecamente thread-safe . Lo stesso vale per l'aggiornamento diretto delle variabili dell'oggetto.


1
Dici "chiamare metodi sull'oggetto a cui fa riferimento non è intrinsecamente thread-safe". Ma come può essere condiviso da due thread l'oggetto a cui fa riferimento un riferimento locale del metodo, istanziato in questo ambito del metodo? Potresti indicare con un esempio?
Akshay Lokur

1
Una variabile locale può contenere o meno un oggetto istanziato nell'ambito del metodo, che non faceva parte della domanda. Anche se lo è, il metodo può accedere allo stato condiviso.
Marko Topolnik

6

Oltre alle altre risposte come quella di Nambari.

Vorrei sottolineare che puoi usare una variabile locale in un metodo di tipo anonimo:

Questo metodo potrebbe essere chiamato in altri thread che potrebbero compromettere la sicurezza del thread, quindi java forza tutte le variabili locali utilizzate in tipi anonimi da dichiarare come finali.

Considera questo codice illegale:

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

Se Java lo consentisse (come fa C # tramite le "chiusure"), una variabile locale non sarebbe più sicura per i thread in tutte le circostanze. In questo caso, inon è garantito che il valore di alla fine di tutti i thread sia 100.


Ciao Weston, dalla discussione sopra e dalle risposte seguenti, ho capito che java garantisce la sicurezza dei thread per tutte le variabili locali. Posso quindi sapere qual è l'effettivo utilizzo della parola chiave sincronizzata? potresti spiegare con un esempio come questo.
Prabhu

5

Il thread avrà il proprio stack. Due thread avranno due stack e un thread non condivide mai lo stack con un altro thread. Le variabili locali vengono memorizzate nello stack di ogni thread. Ciò significa che le variabili locali non vengono mai condivise tra i thread.


3

Fondamentalmente ci sono quattro tipi di archiviazione in Java per memorizzare informazioni e dati sulla classe:

Area del metodo, Heap, Stack JAVA, PC

quindi l'area del metodo e l'heap sono condivisi da tutti i thread ma ogni thread ha il proprio stack JAVA e PC e non è condiviso da nessun altro thread.

Ogni metodo in java è come Stack frame. quindi, quando un metodo viene chiamato da un thread, lo stack frame viene caricato sul suo stack JAVA. Tutte le variabili locali presenti in quello stack frame e lo stack di operandi correlato non sono condivise da altri. Il PC avrà le informazioni della prossima istruzione da eseguire nel codice byte del metodo. quindi tutte le variabili locali sono THREAD SAFE.

@Weston ha anche dato una buona risposta.


1

Solo le variabili locali vengono memorizzate nello stack di thread.

La variabile locale che è primitive type(ad esempio int, long ...) è memorizzata su thread stacke, di conseguenza, l'altro thread non ha accesso ad essa.

La variabile locale che è reference type(successore di Object) contiene da 2 parti: indirizzo (su cui è memorizzato thread stack) e oggetto (su cui è memorizzato 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;
}

inserisci qui la descrizione dell'immagine

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.