Os métodos estáticos não sincronizados são seguros para threads se não modificarem variáveis ​​de classe estática?

145

Eu queria saber se você tem um método estático que não é sincronizado, mas não modifica nenhuma variável estática, é seguro para threads? E se o método criar variáveis ​​locais dentro dele? Por exemplo, o código a seguir é seguro para threads?

public static String[] makeStringArray( String a, String b ){
    return new String[]{ a, b };
}

Portanto, se eu tiver dois threads chamando esse método de forma contínua e simultânea, um com cães (digamos "great dane" e "bull dog") e outro com gatos (digamos "persa" e "siamês") sempre receberei gatos e cães na mesma matriz? Ou os cães e gatos nunca estarão dentro da mesma invocação do método ao mesmo tempo?

Trenó
fonte
outro tópico sobre esse problema: stackoverflow.com/questions/8015797/…
21212
2
Essa é uma pergunta diferente, é se a chamada de método estático é segura para threads, não se matrizes são.
Sled

Respostas:

212

Esse método é 100% seguro para threads, seria mesmo se não fosse static. O problema com a segurança de threads surge quando você precisa compartilhar dados entre threads - você deve cuidar da atomicidade, visibilidade etc.

Este método opera apenas em parâmetros, que residem na pilha e fazem referência a objetos imutáveis ​​na pilha. A pilha é inerentemente local ao encadeamento , portanto, nenhum compartilhamento de dados ocorre.

Objetos imutáveis ​​( Stringneste caso) também são seguros para threads porque, uma vez criados, não podem ser alterados e todos os threads veem o mesmo valor. Por outro lado, se o método estivesse aceitando (mutável), Datevocê poderia ter tido um problema. Dois threads podem modificar simultaneamente a mesma instância do objeto, causando condições de corrida e problemas de visibilidade.

Tomasz Nurkiewicz
fonte
4
Resposta correta. As variáveis ​​no nível do método são replicadas em cada pilha de execução do encadeamento.
Sid
tecnicamente, o método deve ser incorporado e os parâmetros serão registradores da CPU. No entanto, a resposta está correta
bestsss 02/03
43
A pilha é obviamente local para o encadeamento atual, mas você pode ter referências a objetos compartilhados nessa pilha. Isso não é um problema no exemplo, pois Strings são imutáveis, mas um método que modifica um parâmetro passado pode ter problemas de segurança de encadeamento se esse objeto passado estiver acessível a partir de vários encadeamentos.
Jörn Horstmann
1
Como o @TomaszNurkiewicz mencionou, se passarmos uma referência a objetos mutáveis, podemos entrar em condições de corrida. Isso vale mesmo que o método não altere o objeto de forma alguma? Quero dizer, ainda será classificado como uma condição de corrida porque o objeto era mutável? E se adicionarmos a palavra-chave final aos parâmetros?
Rafay
E se eu passar o objeto de classe no método? A vara está na pilha ou pilha?
grep
28

Um método só pode ser inseguro de thread quando altera algum estado compartilhado. Se é estático ou não, é irrelevante.

Konrad Garus
fonte
3
@Konrad_Garus A questão aqui foi sobre se as variáveis ​​locais constituem um estado compartilhado ou não, ou se a pilha para um método estático foi por thread ou compartilhada.
Sled
"Um método só pode ser inseguro quando altera algum estado compartilhado." Não, também pode ser um segmento inseguro se acessar apenas o estado compartilhado sem alterá-lo. O acesso não sincronizado a um objeto mutável pode acessar um estado inconsistente se o objeto estiver sendo alterado por outro thread, mesmo que o outro thread esteja sincronizado corretamente. Ambos os threads precisam de sincronização apropriada para manter a segurança do thread.
22717 Warren Dew
12

A função é perfeitamente segura para threads.

Se você pensar sobre isso ... assuma o que aconteceria se isso fosse diferente. Todas as funções usuais teriam problemas de encadeamento se não fossem sincronizadas, portanto, todas as funções de API no JDK precisariam ser sincronizadas, pois elas poderiam ser chamadas por vários encadeamentos. E como na maioria das vezes o aplicativo usa alguma API, aplicativos multithread seriam efetivamente impossíveis.

Isso é ridículo demais para pensar sobre isso, então, só para você: Os métodos não são seguros para threads, se houver uma razão clara para a ocorrência de problemas. Tente sempre pensar no que se houvesse vários threads na minha função e se você tivesse um depurador de etapas e um passo após o outro avançasse o primeiro ... depois o segundo thread ... talvez o segundo novamente ... haveria problemas? Se você encontrar um, não é um thread seguro.

Esteja ciente de que a maioria das classes Java 1.5 Collection não é segura, exceto aquelas onde declaradas, como ConcurrentHashMap.

E se você realmente quiser mergulhar nisso, observe atentamente a palavra-chave volátil e TODOS os seus efeitos colaterais. Dê uma olhada na classe Semaphore () e Lock () e seus amigos em java.util.Concurrent. Leia todos os documentos da API nas classes. Também vale a pena aprender e satisfazer.

Desculpe por esta resposta excessivamente elaborada.

Daniel
fonte
2
"Se você pensar sobre isso ... assuma o que aconteceria se isso fosse diferente. Todas as funções usuais teriam problemas de segmentação se não fossem sincronizadas, portanto, todas as funções de API no JDK teriam que ser sincronizadas, porque elas poderiam ser chamadas por várias tópicos." Bom ponto!
Sled
1

Use a staticpalavra-chave com métodos estáticos sincronizados para modificar dados estáticos compartilhados entre threads. Com a staticpalavra - chave, todos os threads criados disputarão uma única versão do método.

O uso da volatilepalavra - chave junto com os métodos de instância sincronizada garantirá que cada encadeamento tenha sua própria cópia dos dados compartilhados e nenhuma leitura / gravação vazará entre os encadeamentos.

Clinton
fonte
0

Objetos de sequência imutáveis ​​são outro motivo para o cenário seguro para threads acima. Em vez disso, se objetos mutáveis ​​forem usados ​​(por exemplo, makeMutableArray ..), certamente a segurança do thread será interrompida.

severo
fonte
A questão era se uma chamada de método estático veria os argumentos de outra chamada para o mesmo método de outro encadeamento. A resposta é um sonoro não!". Eu sabia disso, mas queria provar isso a colegas de trabalho duvidosos.
Sled
Por que essa resposta foi rejeitada? O ponto, não contribuído pelas outras respostas, é que a mutabilidade pode estar dentro ou fora da fronteira. Se você retornar um mutável, não será seguro para threads. A questão não a coloca tão estritamente quanto o comentário sugere; a questão estendida após o exemplo de código poderia ter sido expressa em código, talvez como um teste de unidade. Mas simpatizo ao tentar convencer colegas de trabalho. "Eles não acreditam em mim, ou no meu código de teste, ou Josh Bloch, mas talvez aceitem uma resposta no SO".
som-snytt