¿Por qué las variables locales son seguras para subprocesos en Java?

91

Estaba leyendo subprocesos múltiples en Java y me encontré con esto

Las variables locales son seguras para subprocesos en Java.

Desde entonces he estado pensando cómo / por qué las variables locales son seguras para subprocesos.

¿Alguien puede avisarme?

Anand
fuente
27
Porque están asignados en Stack. Y los hilos no comparten la pila ... es único para cada uno ...
Rohit Jain

Respuestas:

103

Cuando creas un hilo, se creará su propia pila. Dos subprocesos tendrán dos pilas y un subproceso nunca comparte su pila con otro subproceso.

A todas las variables locales definidas en su programa se les asignará memoria en la pila (como comentó Jatin, memoria aquí significa, valor de referencia para objetos y valor para tipos primitivos) (Cada llamada al método por un hilo crea un marco de pila en su propia pila). Tan pronto como este hilo complete la ejecución del método, se eliminará el marco de pila.

Hay una gran conferencia del profesor de Stanford en youtube que puede ayudarlo a comprender este concepto.

kosa
fuente
13
Lo siento, estás equivocado, solo las variables locales primitivas se almacenan en la pila. Descanso, todas las variables se almacenan en Heap. Java 7 introdujo el análisis de escape, que para algunas variables podría asignarlo en la pila
Jatin
6
Stack solo contiene la referencia al objeto en el montón. Debido a que la pila se borra, también lo hace la referencia. por lo tanto, está disponible para la recolección de basura
Jatin
6
@Jatin: Tienes razón. Cuando me refiero a la memoria, me refiero al valor de referencia para los objetos y los valores para los primitivos (creo que los desarrolladores novatos también saben que los Objetos están en el montón).
kosa
2
@Nambari pero si el valor de referencia apunta a una variable compartida. Entonces, ¿cómo podemos decir que es seguro para subprocesos?
H.Rabiee
3
@hajder: ¿Qué hace que una variable sea compartida? empezar desde ahí. Cualquiera de las variables de instancia o de clase, ¿verdad? no variables locales Y leer la respuesta de Marko Toplink en este hilo, creo que ese es el punto sobre el que está confundido.
kosa
19

Las variables locales se almacenan en la propia pila de cada hilo. Eso significa que las variables locales nunca se comparten entre hilos. Eso también significa que todas las variables primitivas locales son seguras para subprocesos.

public void someMethod(){

   long threadSafeInt = 0;

   threadSafeInt++;
}

Las referencias locales a objetos son un poco diferentes. La referencia en sí no se comparte. Sin embargo, el objeto al que se hace referencia no se almacena en la pila local de cada hilo. Todos los objetos se almacenan en el montón compartido. Si un objeto creado localmente nunca escapa al método en el que fue creado, es seguro para subprocesos. De hecho, también puede pasarlo a otros métodos y objetos siempre que ninguno de estos métodos u objetos haga que el objeto pasado esté disponible para otros subprocesos.

Renjith
fuente
Hay un error en el grupo, por favor mire los comentarios de la respuesta de @Nambari
Jatin
Si está señalando el hecho de que localSafeInt siempre será 0, luego 1 y luego se eliminará de todos modos, eso es bueno. Por lo tanto, muestra que esta variable no se comparte entre subprocesos y, por lo tanto, no se ve afectada por subprocesos múltiples. Creo que podría señalar un poco más que threadsafe siempre es solo 0 o 1
tObi
14

Piense en métodos como definiciones de funcionalidad. Cuando dos subprocesos ejecutan el mismo método, no están relacionados de ninguna manera. Cada uno creará su propia versión de cada variable local y no podrá interactuar entre sí de ninguna manera.

Si las variables no son locales (como las variables de instancia definidas fuera de un método en el nivel de clase), entonces se adjuntan a la instancia (no a una sola ejecución del método). En este caso, dos subprocesos que ejecutan el mismo método ven la única variable, y esto no es seguro para subprocesos.

Considere estos dos casos:

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

En el primero, dos subprocesos que se ejecutan en la misma instancia de NotThreadsafeverán la misma x. Esto podría ser peligroso, porque los hilos están intentando cambiar x! En el segundo, dos subprocesos que se ejecutan en la misma instancia de Threadsafeverán variables totalmente diferentes y no pueden afectarse entre sí.

Cory Kendall
fuente
6

Cada invocación de método tiene sus propias variables locales y, obviamente, la invocación de un método ocurre en un solo hilo. Una variable que solo se actualiza mediante un único subproceso es inherentemente segura para subprocesos.

Sin embargo , esté atento a lo que se quiere decir exactamente con esto: solo las escrituras en la propia variable son seguras para subprocesos; llamar a métodos en el objeto al que hace referencia no es inherentemente seguro para subprocesos . Lo mismo ocurre con la actualización directa de las variables del objeto.

Marko Topolnik
fuente
1
Dice "llamar a métodos en el objeto al que se refiere no es inherentemente seguro para subprocesos". Pero, ¿cómo el objeto referido por una referencia local de método, instanciado en el alcance de este método, puede ser compartido por dos hilos? ¿Podría señalar con un ejemplo?
Akshay Lokur
1
Una variable local puede o no contener un objeto instanciado dentro del alcance del método, que no era parte de la pregunta. Incluso si lo es, el método puede acceder al estado compartido.
Marko Topolnik
6

Además de las otras respuestas como la de Nambari.

Me gustaría señalar que puede usar una variable local en un método de tipo anónimo:

Este método podría invocarse en otros subprocesos que podrían comprometer la seguridad de los subprocesos, por lo que Java obliga a que todas las variables locales utilizadas en tipos anónimos se declaren como finales.

Considere este código ilegal:

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

Si java permitiera esto (como lo hace C # a través de "cierres"), una variable local ya no sería segura para subprocesos en todas las circunstancias. En este caso, ino se garantiza que el valor de al final de todos los hilos sea 100.

Weston
fuente
Hola Weston, De la discusión anterior y las respuestas a continuación, entendí que Java garantiza la seguridad de subprocesos para todas las variables locales. ¿Puedo saber entonces cuál es el uso real de la palabra clave sincronizada? ¿Podría explicarnos con un ejemplo como este?
Prabhu
5

El hilo tendrá su propia pila. Dos subprocesos tendrán dos pilas y un subproceso nunca comparte su pila con otro subproceso. Las variables locales se almacenan en la propia pila de cada hilo. Eso significa que las variables locales nunca se comparten entre hilos.

Sudhirkumar Murkute
fuente
3

Básicamente, hay cuatro tipos de almacenamiento en java para almacenar información y datos de clase:

Área de método, montón, pila JAVA, PC

por lo tanto, el área de método y el montón son compartidos por todos los subprocesos, pero cada subproceso tiene su propia pila JAVA y PC y eso no es compartido por ningún otro subproceso.

Cada método en java es como marco de pila. entonces, cuando un método es llamado por un hilo, ese marco de pila se carga en su Pila JAVA. Todas las variables locales que están allí en ese marco de pila y la pila de operandos relacionados no son compartidas por otros. La PC tendrá información de la siguiente instrucción a ejecutar en el código de bytes del método. por lo que todas las variables locales son THREAD SAFE.

@Weston también ha dado una buena respuesta.

Prashant
fuente
1

Solo las variables locales se almacenan en la pila de subprocesos.

La variable local que es primitive type(por ejemplo, int, long ...) se almacena en el thread stacky, como resultado, otro hilo no tiene acceso a él.

La variable local que es reference type(sucesora de Object) contiene 2 partes: la dirección (que se almacena en thread stack) y el objeto (que se almacena en 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;
}

ingrese la descripción de la imagen aquí

yoAlex5
fuente