Por que as variáveis ​​locais são thread-safe em Java

90

Eu estava lendo multi-threading em Java e me deparei com isso

Variáveis ​​locais são thread-safe em Java.

Desde então, tenho pensado em como / por que variáveis ​​locais são thread-safe.

Alguém pode me avisar.

Anand
fonte
27
Porque eles são alocados em Stack. E os tópicos não compartilham pilha .. é exclusivo para cada ..
Rohit Jain

Respostas:

103

Ao criar um thread, ele terá sua própria pilha criada. Dois threads terão duas pilhas e um thread nunca compartilha sua pilha com outro thread.

Todas as variáveis ​​locais definidas em seu programa terão memória alocada na pilha (como Jatin comentou, memória aqui significa, valor de referência para objetos e valor para tipos primitivos) (Cada chamada de método por um thread cria um frame de pilha em sua própria pilha). Assim que a execução do método for concluída por esta thread, o stack frame será removido.

Há uma ótima palestra do professor de Stanford no youtube que pode ajudá-lo a entender esse conceito.

kosa
fonte
13
Sinto muito, você está errado, apenas variáveis ​​locais primitivas são armazenadas na pilha. Resto, todas as variáveis ​​são armazenadas no Heap. Java 7 introduziu a análise de escape, que para algumas variáveis ​​pode alocá-la na pilha
Jatin,
6
Stack contém apenas a referência ao objeto no heap. Como a pilha é limpa, a referência também. portanto, está disponível para coleta de lixo
Jatin,
6
@Jatin: Você está correto. Quando me refiro à memória, quero dizer valor de referência para objetos e valores para primitivos (acho que os desenvolvedores novatos também sabem que os objetos estão no heap).
kosa de
2
@Nambari mas se o valor de referência apontar para uma variável compartilhada. Então, como podemos dizer que é thread-safe?
H.Rabiee
3
@hajder: O que torna uma variável compartilhada? comece a partir daí. Variáveis ​​de instância ou classe, certo? variáveis ​​não locais E leia a resposta de Marko Toplink neste tópico, acho que é esse o ponto sobre o qual você está confuso.
kosa
19

Variáveis ​​locais são armazenadas na própria pilha de cada thread. Isso significa que variáveis ​​locais nunca são compartilhadas entre threads. Isso também significa que todas as variáveis ​​primitivas locais são thread-safe.

public void someMethod(){

   long threadSafeInt = 0;

   threadSafeInt++;
}

As referências locais a objetos são um pouco diferentes. A referência em si não é compartilhada. O objeto referenciado, entretanto, não é armazenado na pilha local de cada thread. Todos os objetos são armazenados no heap compartilhado. Se um objeto criado localmente nunca escapar do método em que foi criado, ele é seguro para threads. Na verdade, você também pode passá-lo para outros métodos e objetos, desde que nenhum desses métodos ou objetos torne o objeto passado disponível para outras threads

Renjith
fonte
Há um erro no argumento, veja os comentários da resposta de @Nambari
Jatin
Se você está apontando para o fato de que localSafeInt também será apenas 0, então 1 e então excluído de qualquer maneira, isso é bom. Isso mostra que essa variável não é compartilhada entre os threads e, portanto, não é afetada pelo multi threading. Acho que você poderia apontar um pouco mais que threadsafe é sempre apenas 0 ou 1
obi
14

Pense em métodos como definições de funcionalidade. Quando dois threads executam o mesmo método, eles não estão de maneira alguma relacionados. Cada um deles criará sua própria versão de cada variável local e não poderão interagir uns com os outros de nenhuma forma.

Se as variáveis ​​não são locais (como variáveis ​​de instância definidas fora de um método no nível de classe), então elas são anexadas à instância (não a uma única execução do método). Nesse caso, dois threads executando o mesmo método veem a mesma variável, e isso não é seguro para threads.

Considere estes dois 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;
    }
}

No primeiro, dois threads em execução na mesma instância de NotThreadsafeverão o mesmo x. Isso pode ser perigoso, porque os tópicos estão tentando mudar x! No segundo, dois threads em execução na mesma instância de Threadsafeverão variáveis ​​totalmente diferentes e não podem afetar um ao outro.

Cory Kendall
fonte
6

Cada invocação de método possui suas próprias variáveis ​​locais e, obviamente, uma invocação de método ocorre em um único thread. Uma variável que é atualizada apenas por um único thread é inerentemente segura para thread.

No entanto , fique atento ao que exatamente significa isso: apenas as gravações na variável em si são seguras para threads; chamar métodos no objeto a que se refere não é inerentemente thread-safe . O mesmo vale para a atualização direta das variáveis ​​do objeto.

Marko Topolnik
fonte
1
Você diz que "chamar métodos no objeto a que se refere não é inerentemente thread-safe". Mas, como o objeto referido por uma referência local de método - instanciado neste escopo de método - pode ser compartilhado por duas threads? Você poderia apontar por exemplo?
Akshay Lokur
1
Uma variável local pode ou não conter um objeto instanciado dentro do escopo do método, que não fazia parte da questão. Mesmo se for, o método pode acessar o estado compartilhado.
Marko Topolnik
6

Além das outras respostas, como a de Nambari.

Gostaria de salientar que você pode usar uma variável local em um método do tipo anônimo:

Este método pode ser chamado em outras threads que podem comprometer a threadsafety, então java força todas as variáveis ​​locais usadas em tipos anônimos a serem declaradas como finais.

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

Se o java permitisse isso (como o C # faz por meio de "fechamentos"), uma variável local não seria mais threadsafe em todas as circunstâncias. Nesse caso, o valor de ino final de todos os threads não tem garantia de ser 100.

Weston
fonte
Oi Weston, A partir da discussão acima e das respostas abaixo, entendi que o java garante threadsafety para todas as variáveis ​​locais. Posso saber qual é o uso real da palavra-chave sincronizada então? você poderia explicar com um exemplo como este.
Prabhu
5

O thread terá sua própria pilha. Dois threads terão duas pilhas e um thread nunca compartilha sua pilha com outro thread. Variáveis ​​locais são armazenadas na própria pilha de cada thread. Isso significa que variáveis ​​locais nunca são compartilhadas entre threads.

Sudhirkumar Murkute
fonte
3

Basicamente, quatro tipos de armazenamento existem em java para armazenar informações e dados da classe:

Área de Método, Pilha, Pilha JAVA, PC

portanto, a área do método e o heap são compartilhados por todos os threads, mas cada thread tem sua própria pilha JAVA e PC e isso não é compartilhado por nenhum outro thread.

Cada método em java é como Stack frame. portanto, quando um método é chamado por um thread, esse quadro de pilha é carregado em sua pilha JAVA. Todas as variáveis ​​locais que estão nesse quadro de pilha e a pilha de operandos relacionados não são compartilhados por outros. O PC terá informações da próxima instrução a ser executada no código de bytes do método. então todas as variáveis ​​locais são THREAD SAFE.

@Weston também deu uma boa resposta.

Prashant
fonte
1

Apenas variáveis locais são armazenadas na pilha de threads.

A variável local que é primitive type(por exemplo, int, long ...) é armazenada no thread stacke como resultado - outro segmento não tem acesso a ele.

A variável local que é reference type(sucessora de Object) contém 2 partes - endereço (que é armazenado em thread stack) e o objeto (que é armazenado em 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;
}

insira a descrição da imagem aqui

yoAlex5
fonte