Palavra -chave sincronizada
A chave sincronizada pode modificar funções e instruções nas funções. Seja adicionado a métodos ou objetos, o bloqueio adquire é um objeto, em vez de tratar um pedaço de código ou função como um bloqueio.
1. Quando dois threads simultâneos acessam o bloco de código sincronizado (this) sincronizado no mesmo objeto, apenas um thread pode ser executado por um período de tempo e o outro thread pode executar apenas esse código após o thread atual concluir a execução.
2. Quando um thread acessa um bloco de código sincronizado sincronizado (this) em um objeto, outros threads ainda podem acessar outros blocos de código não sincronizados (este) neste objeto.
3. Deve -se notar aqui que, quando um thread acessar um bloco de código sincronizado (este) de um objeto, outros threads serão impedidos de acessar outros blocos de código sincronizado sincronizados (this) neste objeto.
4. O acima também é aplicável a outros blocos de código de sincronização, ou seja, quando um thread acessa um bloco de código de sincronização sincronizado (this) de um objeto, o thread obtém o bloqueio do objeto do objeto. Além disso, cada objeto (isto é, a instância da classe) corresponde a um bloqueio. Ele pertence está bloqueado. Esse mecanismo garante que, ao mesmo tempo, para cada objeto, no máximo uma de todas as funções de membro declaradas sincronizadas está em um estado executável (porque no máximo um thread pode adquirir a trava do objeto), evitando o acesso ao conflito de variáveis de membro da classe .
Desvantagens do método sincronizado:
Como a sincronizada trava o objeto que chama esse método de sincronização, ou seja, quando um encadeamento P1 executa esse método em diferentes encadeamentos, eles formarão exclusões mútuas, alcançando assim o efeito da sincronização. Mas deve -se notar aqui que outro objeto de classe que é desse objeto pode chamar arbitrariamente esse método com a palavra -chave sincronizada adicionada. A essência do método de sincronização é aplicar a referência de objeto. Este caso. Vamos explicar esta situação em detalhes abaixo:
Primeiro, vamos introduzir dois objetos bloqueados com a palavra -chave sincronizada: objeto e classe - sincronizados podem adicionar bloqueios de objeto ou bloqueios de classe aos recursos. Desta classe, outros objetos desta classe ainda podem usar o método sincronizado que bloqueou o objeto anterior.
Uma das principais questões que discutimos aqui é: "A mesma classe e diferentes instâncias chamarão o mesmo método, haverá um problema de sincronização?"
O problema de sincronização está relacionado apenas ao recurso e depende se o recurso é estático. Para os mesmos dados estáticos, sua mesma função pertence a diferentes threads para ler e gravar ao mesmo tempo, e a CPU não gera erros. você. Mesmo se você tiver dois códigos diferentes em execução em dois núcleos diferentes da CPU e escrevendo um endereço de memória ao mesmo tempo, o mecanismo de cache bloqueará um em L2 primeiro. Em seguida, atualize e compartilhe com outro núcleo, e não haverá erros, caso contrário, a Intel ou a AMD será em vão.
Portanto, desde que você não tenha o mesmo recurso ou variável que dois compartilhamentos de código, não haverá inconsistência de dados. Além disso, as chamadas para diferentes objetos da mesma classe têm pilhas completamente diferentes e são completamente irrelevantes.
Aqui, usamos um exemplo para ilustrar o processo de vendas de ingressos, onde nossos recursos compartilhados são o número restante de ingressos.
pacote com.test; classe pública ThreadSafest estende o thread implementa o runnable {private static int num = 1; Void Sell (Nome da String) {if (num> 0) {System. (Concluído em cerca de 5 segundos). ; ("Sistema: Número atual de votos:" NUM); args []) {tente {new ThreadSafest ("Ticket Seller Li XX") .Start (); {E.PrintStackTrace (); Execute o código acima e a saída que obtemos é:
Condutor de ingressos LI XX: O número de bilhetes de teste é superior a 0 condutor de ingressos LI xx: o pagamento está sendo cobrado (concluído em cerca de 5 segundos). . . Condutor de ingressos King X: O número de bilhetes de teste é superior a 0 ticket condutor rei X: o pagamento está sendo cobrado (concluído em cerca de 5 segundos). . . Vendedor de ingressos LI XX: Imprima a fatura, sistema de conclusão de vendas de ingressos: Número atual de votos: 0 vendedor de ingressos Wang X: Imprima a conta, o sistema de conclusão de vendas de ingressos: Número atual de votos: -1 Aviso: o número de votos é menor que 0, números negativos aparecem
Com base nos resultados da saída, podemos descobrir que os votos restantes são -1 e há um problema de erro de sincronização. A razão para isso é que os dois objetos de instância que criamos modificaram o recurso estático compartilhado estático int num = 1 ao mesmo tempo. Em seguida, removemos o modificador estático na caixa no código acima e, em seguida, executamos o programa para obter:
Condutor de ingressos LI XX: O número de bilhetes de teste é superior a 0 condutor de ingressos LI xx: o pagamento está sendo cobrado (concluído em cerca de 5 segundos). . . Condutor de ingressos King X: O número de bilhetes de teste é superior a 0 ticket condutor rei X: o pagamento está sendo cobrado (concluído em cerca de 5 segundos). . . Vendedor de ingressos LI XX: Imprima a conta, Sistema de conclusão de vendas de ingressos: Número atual de ingressos: 0 vendedor de ingressos Wang X: Imprima a conta, o sistema de conclusão de vendas de ingressos: Número atual de ingressos: 0
Depois de modificar o grau, o programa é executado sem problemas. Mas isso vai contra nossa expectativa de que vários threads possam lidar com recursos compartilhados ao mesmo tempo (após estática, o NUM muda de recursos compartilhados para variáveis de membros pertencentes a cada instância), o que obviamente não é o que queremos.
Nos dois códigos acima, a principal coisa a adotar é bloquear o objeto. Pelo motivo que mencionei anteriormente, quando duas instâncias diferentes de uma classe modificam o mesmo recurso compartilhado, a CPU padrão é a lógica do programa. Portanto, precisamos alterar o escopo da trava. Classe e os recursos compartilhados ao mesmo tempo.
pacote com.test; classe pública ThreadSafest estende o thread implementa o runnable {private static int num = 1; Void Static Sell (Nome da String) {if (num> 0) {System. Colecionar o pagamento (cerca de 5 segundos concluídos). (); .println ("Sistema: Contagem de votos atuais:" NUM); String args []) {tente {new ThreadSafeTest ("Ticket Seller Li XX") .start (); ) {E.PrintStackTrace (); Faça o programa como acima para obter o resultado da execução:
Condutor de ingressos LI XX: O número de bilhetes de teste é superior a 0 condutor de ingressos LI xx: o pagamento está sendo cobrado (concluído em cerca de 5 segundos). . . Vendedor de ingressos LI XX: Imprima o ingresso, o sistema de conclusão de vendas de ingressos: Número atual de ingressos: 0 vendedor de ingressos Wang x: sem ingressos, interromper as vendas de ingressos
Um modificador estático é adicionado ao método Sell (), para que o objeto do bloqueio se torne uma classe. Isso obterá os resultados que desejamos conforme o esperado.
Resumir:
1. Existem dois usos de palavras -chave sincronizadas: método sincronizado e bloco sincronizado.
2. Em Java, não é apenas uma instância de classe, mas cada classe também pode corresponder a um bloqueio.
1. A palavra -chave sincronizada não pode ser herdada. Embora sincronizado possa ser usado para definir métodos, o sincronizado não pertence a uma parte da definição do método, portanto a palavra -chave sincronizada não pode ser herdada. Se um método na classe pai usa a palavra -chave sincronizada e a subclasse também substituir esse método, por padrão, o método na subclasse não será sincronizado e deve ser exibido para adicionar o método na subclasse. . Obviamente, você também pode chamar os métodos correspondentes na classe pai na subclasse. sendo sincronizado. como,
Adicione a palavra -chave sincronizada à subclasse:
classe pai {public sincronizado void method () {}} classe Child estende o pai {public sincronizado void métod () {}} Ligue para o método da classe pai:
classe pai {public sincronizado void method () {}} classe Child estende o pai {public void Method () {super.method (); 2. A palavra -chave sincronizada não pode ser usada ao definir o método da interface.
3. O construtor não pode usar a palavra -chave sincronizada, mas o bloco sincronizado pode ser usado para sincronização.
4. A posição sincronizada pode ser colocada livremente, mas não pode ser colocada atrás do tipo de retorno do método.
5. A palavra -chave sincronizada não pode ser usada para sincronizar variáveis, como o código a seguir está incorreto:
público sincronizado int n = 0;
6. Embora o uso da palavra -chave sincronizada seja o método de sincronização mais seguro, se usado em grandes quantidades, também causará consumo desnecessário de recursos e perdas de desempenho. Na superfície, trava um método sincronizado, mas, de fato, trava uma classe. executado. Os métodos estáticos são semelhantes aos métodos não estáticos. No entanto, métodos estáticos e métodos não estáticos não se afetarão, consulte o seguinte código:
classe pública mythread1 estende thread {public string MethodName; ");} public sincronizado void method2 () {método (" método não estático do método2 ");} public static sincronizado void method3 () {método (" método static3 ");} public static sincronizado void métod4 () {método ("Método estático4 Método"); ) Exceção {mythread1 mythread1 = novo mythread1 (); ); O resultado da operação é:
Método não estático1 Método estático 3 Método3
Nos resultados da execução acima, podemos ver que o método2 e o método4 não serão executados até que o método1 e o método3 sejam concluídos. Portanto, podemos tirar uma conclusão de que, se usarmos sincronizados para definir métodos não estáticos na classe, isso afetará todos os métodos não estáticos definidos nessa classe; Nesta classe, o método estático definido por sincronizado. Isso é um pouco como um bloqueio de tabela em uma tabela de dados. Portanto, o uso pesado desse método de sincronização reduzirá bastante o desempenho do programa.
Dicas para acesso mais seguro a recursos compartilhados:
1. Defina a variável de instância do método privado + seu método GET, em vez de definir a variável de instância do público/protegido. Se uma variável for definida como pública, o objeto poderá obtê -lo diretamente ignorando o controle do método de sincronização no mundo exterior e alterá -lo. Esta também é uma das implementações padrão do Javabean.
2. Se a variável da instância for um objeto, como uma matriz ou uma lista de Arraylist, o método acima ainda é inseguro, porque quando o mundo exterior recebe a referência ao objeto da instância através do método Get e aponta para outro objeto, então A variável privada também é alterada, não seria muito perigoso? No momento, você precisa adicionar sincronizado para obter o método e retornar apenas clone () deste objeto privado. Dessa forma, o que o chamador recebe é apenas uma referência à cópia do objeto.
Três maneiras de obter o Monitor de Objetos (Lock) e notificar ()
Em um método de encadeamento, as chamadas para esperar () e notificar () devem especificar um objeto, e o thread deve ter o monitor do objeto. A maneira mais fácil de obter o monitor de objeto é usar a palavra -chave sincronizada no objeto. Depois de chamar o método wait (), o thread liberará o bloqueio do objeto e entrará no estado do sono. Quando outros threads chamam o método notify (), o mesmo objeto deve ser usado. Após o método notify (), o thread de espera correspondente no objeto será despertado.
Para vários métodos bloqueados por um objeto, um deles será selecionado para acordar ao chamar o método notify () e notifyAll () acordará todos os seus threads de espera.
pacote net.mindview.util; importar javax.swing.jframe; public class waitandnotify {public static void main (string [] args) {system. frame.setDefaultCloseoperation (jframe. Exit_on_close); 300, 100); {t = novo e não .Add (Start); (pausa); .Add (final); = f; if (iswait) wait (); Como no código na caixa de exemplo acima, se o bloco de código síncrono for removido, a execução lançará uma exceção java.lang.illegalmonitorStateException.
Olhando para o JDK, podemos ver que o motivo dessa exceção é que o thread atual não é o proprietário deste monitor de objeto.
Este método deve ser chamado apenas por um thread que é o proprietário deste monitor de objeto.
1. Ao executar o método de instância síncrona desse objeto, como:
public sincronizado void n () {notify (); 2. Ao executar o corpo da declaração sincronizada que é sincronizada nesse objeto, como:
public void n () {sincronizado (this) {notify (); 3. Para objetos do tipo de classe, você pode executar métodos estáticos síncronos desta classe.
Ao chamar um método estático, não criamos necessariamente um objeto de instância. Portanto, isso não pode ser usado para sincronizar métodos estáticos, portanto o objeto de classe deve ser usado para sincronizar os métodos estáticos. Outro exemplo para ilustrar:
classe pública Sincronizada implementa runnable {sinalizador booleano estático privado = true; // classe de sincronização do objeto Método um: // preste atenção ao método de sincronização da modificação estática, monitor: synchronizedstatic.class prate vato sincronizado estático void testsyncmethod () {para (int vate I = 0; // Classe Método de Sincronização do Objeto 2: Void privado testSyncBlock () {// Display usa a classe Get como o monitor. É o mesmo que o método sincronizado estático recebe implicitamente o monitor de classe. Sincronizado (Syncronizedstatic ("testsyncblock:" + i); Portanto, threads diferentes executarão métodos diferentes e somente dessa maneira você pode ver diferentes efeitos de travamento. se (flag) = novo sincronizado O código acima executa o resultado da execução de dois métodos de sincronização para imprimir 100 números de 0 a 99 ao mesmo tempo. Block. Os dois métodos são semelhantes. Como o escopo do método e o método dois são as duas classes, eles são mutuamente exclusivos. Portanto, o resultado em execução do programa será:
TestSyncMethod: 0TestSyncMethod: 1 ... ... TestSyncMethod: 99TestSyncBlock: 0 ... ... TestSyncblock: 99
No entanto, se substituirmos o SyncronizedStatic.
TestSyncBlock: 0TestSyncMethod: 0TestSyncBlock: 1TestSyncMethod: 1 ... ... TestSyncMethod: 99TestSyncblock: 99
Existem dois escopos de bloqueios, um é o objeto da classe e o outro é a própria classe. No código acima, dois métodos são fornecidos para tornar o escopo da trava uma classe, para que a sincronização possa ser concluída entre diferentes objetos da mesma classe.
Para resumir o exposto, os seguintes pontos precisam ser observados:
1. Wait (), notify () e notifyAll () precisam ser executados sob a premissa de ter o monitor de objeto, caso contrário, um java.lang.illegalmonitorStateException será lançado.
2. Vários threads podem esperar em um objeto ao mesmo tempo.
3. Notify () é acordar aleatoriamente um thread aguardando o objeto.
4. O thread que é despertado pelo notify () não acorda imediatamente após o notificação () é executado, mas somente após o thread notify () libera o monitor de objeto.
5. Esses métodos de objeto ainda estão longe dos métodos de sono e interrupção do Thread, portanto, não os confundem.