Como sincronizar métodos ao usar threads java
Sempre que você trabalha em um programa Java que usa threads, você tem que considerar a questão desagradável de concorrência. Em particular, o que se dois threads tenta aceder a um método de um objeto precisamente ao mesmo tempo? A menos que você programar com cuidado, o resultado pode ser desastroso. Um método que realiza um cálculo simples retorna resultados imprecisos.
Em um aplicativo de banco online, você pode descobrir que alguns depósitos são creditados duas vezes e alguns levantamentos não são creditados em tudo. Em um sistema de encomendas on-line, a ordem de um cliente pode ficar gravado na conta de um cliente diferente.
A chave para lidar com problemas de simultaneidade é reconhecer métodos que atualizar dados e que pode ser chamado por mais de um segmento. Depois de identificar esses métodos, a solução é simples. Você acabou de adicionar o sincronizado palavra-chave para a declaração de método, assim:
pública sincronizado someMethod void () ...
Este código diz Java para colocar um trancar no objecto de modo que não há outros métodos pode ligar quaisquer outros métodos sincronizados para o objecto até este método acaba. Em outras palavras, ele desativa temporariamente multithreading para o objeto.
Aqui estão alguns exemplos concretos. Este código cria uma instância da CountDownClock classe.
DoTwoThings classe importação java.util.concurrent.ScheduledThreadPoolExecutor-públicas {ScheduledThreadPoolExecutor piscina = new ScheduledThreadPoolExecutor (2) -CountDownClock relógio = new CountDownClock (20) -public static void main (String [] args) {novas DoTwoThings () -} DoTwoThings ( ) {pool.execute (relógio) -pool.execute (relógio) -pool.shutdown () -}}
A saída resultante é uma miscelânea imprevisível de saídas de dois fios, com algumas das contagens duplicados e outros totalmente ignorada, como este:
T menos 20T menos 20T menos 19T menos 19T menos 18T menos 17T menos 16T menos 15T menos 13T menos 13T menos 12T menos 12T menos 11T menos 11T menos 10T menos T9 menos 7T menos 7T menos T6 menos T5 menos 4T menos 3T menos 2T minus minus 2T 1T menos 0
Os dois segmentos executar seus laços ao mesmo tempo, então depois de um segmento exibe seu T menos 20, o outro segmento exibe seu próprio T menos 20. A mesma coisa acontece para T menos 19, T menos 18, e assim por diante.
Em seguida, este código gera dois tópicos, cada um dos quais executa uma cópia do CountDownClock O código de exemplo.
classe de importação java.util.concurrent.ScheduledThreadPoolExecutor-pública DoTwoThingsSync {piscina ScheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor (2) -CountDownClockSync relógio = new CountDownClockSync (20) -public static void main (String [] args) {new DoTwoThingsSync () -} DoTwoThingsSync ( ) {pool.execute (relógio) -pool.execute (relógio) -pool.shutdown () -}}
Java de sincronizado palavra-chave garante que apenas um thread por vez chama o corre método. A saída resultante mostra uma execução completa do corre Método seguido por outro.
classe CountDownClockSync estende Thread {private int start-pública CountDownClockSync (início int) {this.start = iniciar-} sincronizado public void run () {for (int t = iniciar- t gt; = 0- t -) {System.out.println ("menos T " + T) -tentar {Thread.sleep (1000) -} catch (InterruptedException e) {}}}}
chamadas as duas linhas à corre método não estão intercalados, de modo que as contagens de saída para baixo a partir de 20 para 0 e, em seguida, faz a contagem regressiva pela segunda vez desde 20 para 0:
T menos 19T 20T minus minus 18
E assim por diante, até
T menos 2T menos 1T menos 0T menos 19T 20T minus minus 18
E assim por diante, até
T menos 1T 2T minus minus 0
A parte difícil é saber quais métodos para sincronizar. Qualquer método que atualiza variáveis de instância está em risco - e precisa ser sincronizado. Isso porque quando dois ou mais threads executar um método, ao mesmo tempo, os fios têm uma cópia comum de variáveis de instância do método.
Mesmo métodos que consistem em apenas uma linha de código estão em risco. Considere este método:
int sequenceNumber = 0-public int getNextSequenceNumber () {return sequenceNumber ++ -}
Você acha que porque este método tem apenas uma declaração, algum outro segmento não pôde interrompê-lo no meio. Infelizmente, esse não é o caso. Este método deve obter o valor do número sequencial campo, adicione 1 a ele, salvar o valor atualizado de volta para o número sequencial campo, e retornar o valor.
Na verdade, esta única instrução Java compila a 11 instruções de bytecode. Se o segmento é preemptado entre qualquer dessas bytecodes por outro segmento que chama o mesmo método, os números de série se munged.
Por razões de segurança, porque não basta fazer todos os métodos sincronizados? Você tem duas razões para não fazê-lo:
Sincronizando métodos leva tempo. Java tem de adquirir um bloqueio no objeto que está sendo sincronizado, executar o método, em seguida, liberar o bloqueio. Mas antes que ele possa fazer isso, tem que verificar para se certificar de que algum outro segmento não já tem um bloqueio no objeto. Todo este trabalho leva tempo.
Mais importante, a sincronização de todos os seus métodos derrota o propósito de multithreading, assim você deve sincronizar somente os métodos que necessitam dele.
o sincronizado O palavra-chave não bloquear todo o acesso a um objeto. Outros tópicos ainda pode executar métodos não sincronizadas do objeto enquanto o objeto está bloqueado.
o Objeto classe fornece três métodos que podem deixar objetos sincronizadas coordenar suas atividades. o esperar método coloca uma linha no estado de espera até que algum outro segmento chama um objeto de notificar ou (mais comumente) notifyAll método. Estes métodos são úteis quando um thread tem que esperar por outro segmento que fazer algo antes que ele possa prosseguir.
O exemplo clássico é um sistema bancário em que uma thread faz saques e o outro faz depósitos. Se o saldo da conta de um cliente cai para zero, o segmento que faz levantamentos pode chamar esperar- em seguida, o fio que faz depósitos pode chamar notifyAll. Dessa forma, cada vez que um depósito é feito, o segmento de retirada poderá verificar o saldo da conta do cliente para ver se ele agora tem dinheiro suficiente.