Comportamento do método estático em ambiente multi-threaded em java

114

Há uma pergunta simples e estúpida que me incomoda e traz vários argumentos em minha mente. Quero tirar todas as dúvidas sobre as questões abaixo.

class Clstest{

    public static String testStaticMethod(String inFileStr) {

        // section 0

        // section 1

        // do something with inFileStr

        // section 2

        // section 3

        return inFileStr;

    }

}

Vamos supor que cinco threads estão executando uma chamada para Clstest.testStaticMethod("arg-n")ao mesmo tempo.

Thread 1 chama Clstest.testStaticMethod("arg-1").

Quando o thread 1 está na seção 1, o thread 2 chama Clstest.testStaticMethod("arg-2").

Então, o que acontecerá com o Tópico 1? Ele irá para o estado de suspensão?

Quando o Thread 1 tiver a chance, ele retomará a execução da seção 1 onde foi pausado?

Como isso acontece quando há um Clstest.testStaticMethode o mesmo Clstest.testStaticMethodé compartilhado entre todos os cinco tópicos?

Existe alguma possibilidade de trocar o inFileStrenviado por vários threads?

namalfernandolk
fonte
Qual idioma você está alvejando?
ΩmegaMan
3
@ OmegaMan: é java
namalfernandolk

Respostas:

192

A resposta de Hans Passant é boa. Mas pensei em tentar explicar em um nível um pouco mais simples para qualquer pessoa que se deparar com isso e for novato em Java. Aqui vai ..

A memória em java é dividida em dois tipos - heap e pilhas. O heap é onde todos os objetos vivem e as pilhas são onde os threads fazem seu trabalho. Cada thread tem sua própria pilha e não podem acessar as pilhas umas das outras. Cada thread também tem um ponteiro para o código que aponta para o trecho de código que eles estão executando no momento.

Quando um thread começa a executar um novo método, ele salva os argumentos e as variáveis ​​locais desse método em sua própria pilha. Alguns desses valores podem ser ponteiros para objetos no heap. Se duas threads estiverem executando o mesmo método ao mesmo tempo, ambas terão seus ponteiros de código apontando para esse método e terão suas próprias cópias de argumentos e variáveis ​​locais em suas pilhas. Eles só interferirão uns com os outros se as coisas em suas pilhas apontarem para os mesmos objetos na pilha. Nesse caso, todo tipo de coisa pode acontecer. Mas, como Hans aponta, Strings são imutáveis ​​(não podem ser alteradas), então estamos seguros se este for o único objeto sendo "compartilhado".

Muitos threads podem estar executando o mesmo método. Eles podem não estar sendo executados ao mesmo tempo - isso depende de quantos núcleos você tem em sua máquina, pois a JVM mapeia os encadeamentos Java para os encadeamentos do SO, que são planejados nos encadeamentos de hardware. Portanto, você tem pouco controle sobre a maneira como esses threads se intercalam sem usar mecanismos de sincronização complexos .

Observe que dormir é algo que um thread faz a si mesmo.

Selig
fonte
3
Portanto, em um ambiente de processador multi-core, pode haver vários threads executando o mesmo código ao mesmo tempo, não é? E em um ambiente de processador único, há apenas um thread em execução por vez. (vários encadeamentos compartilhando o tempo entre eles.) Portanto, quando o encadeamento escalonado dá a chance do encadeamento em execução atual (A) para o encadeamento (B), como o encadeamento (A) é retomado de onde foi pausado? Quero dizer, como ele sabe o ponto de retomada? É por causa de "Cada thread também tem um ponteiro para o código que aponta para o pedaço de código que eles estão executando atualmente?" como você disse?
namalfernandolk
6
Você conseguiu. Apenas para esclarecer alguns pontos - em primeiro lugar, como os threads são agendados está fora do controle do Java. Estou falando sobre o Hotspot JVM da Sun aqui. A JVM mapeia um encadeamento Java para um encadeamento do sistema operacional e o sistema operacional decide quais encadeamentos executar. Como você disse, em uma máquina com um único núcleo, o sistema operacional só pode ser executado um por vez, mas em uma máquina com vários núcleos pode ser executado mais de um ao mesmo tempo. Em segundo lugar, um thread não está realmente ciente de quando é pausado, a única informação que possui é o ponteiro do programa (o ponteiro para o código) e pilha, que são salvos e restaurados exatamente como estavam.
seleção de
Portanto, a interferência entre threads acontece quando as threads estão usando variáveis ​​fora de seu escopo local e, por exemplo, uma thread atualiza o valor da variável antes que outra thread extraia essa variável (ou um ponteiro para a variável) em sua própria pilha? Esse é um entendimento correto?
hariszhr
2
Para ser um pouco mais preciso ... a interferência só pode acontecer e pode acontecer, mas não necessariamente acontecer ... quando os threads compartilham coisas no heap (não local). Existem algumas maneiras diferentes de ocorrer a interferência, que dependem das dependências entre as diferentes partes do código. Não tenho certeza sobre o seu exemplo, pois faltam alguns detalhes. Talvez você esteja se referindo a um problema potencial em que os threads A e B leem um valor compartilhado e, em seguida, o atualizam com base no valor lido. Esta é uma corrida de dados.
seleção de
1
@selig se eu tiver uma classe com apenas métodos de instância para ex: uma classe de serviço e é singleton, então não preciso me preocupar com vários threads executando os métodos de instância ao mesmo tempo, já que a classe de serviço não mantém nenhum estado onde estado está presente apenas se a classe tiver variáveis ​​de instância. Meu entendimento está correto?
Yug Singh
67

Ele irá para o estado de suspensão?

Não, a execução de um encadeamento não afeta outros encadeamentos, desde que eles não sejam sincronizados intencionalmente entre si. Se você tiver mais de um núcleo de processador, todas as máquinas recentes têm, esses threads provavelmente serão executados exatamente ao mesmo tempo. Isso se torna um pouco menos provável quando você inicia 5 threads, pois sua máquina pode não ter núcleos suficientes. O sistema operacional é forçado a escolher entre eles, dando a cada um deles algum tempo para serem executados. O trabalho do planejador de thread. Um encadeamento não estará em um estado de "suspensão", ele simplesmente será pausado e aguardará o agendador do encadeamento para dar uma chance de execução. Ele continuará onde foi interrompido pelo planejador.

Existe alguma possibilidade de trocar o inFileStr enviado por vários threads?

Não existe essa possibilidade, as threads têm sua própria pilha, portanto, qualquer argumento de método e variável local será exclusivo para cada thread. Usando umAlém disso, o string garante que essas threads não interfiram umas com as outras, pois as strings são imutáveis.

Essa garantia não existe se o argumento for uma referência a outro tipo de objeto mutável. Ou se o próprio método usa variáveis ​​que são estáticas ou referências a objetos no heap. A sincronização é necessária quando um thread modifica o objeto e outro thread o lê. A palavra-chave lock na linguagem C # é a maneira padrão de implementar a sincronização necessária. O fato de o método ser estático não significa que essa sincronização nunca seja necessária. Menos provável, pois você não precisa se preocupar com threads acessando o mesmo objeto (compartilhando isso ).

Hans Passant
fonte
3
Ops, nunca vi a tag [java]. Perto o suficiente.
Hans Passant
Esqueci de adicionar quando foi postado. Foi mal. :). De qualquer forma, obrigado pela resposta. Foi muito útil.
namalfernandolk