Antes do Java 5, a palavra -chave sincronizada foi usada para implementar a função de bloqueio.
A palavra -chave sincronizada pode ser usada como um modificador (método sincronizado) ou como uma instrução dentro de uma função (bloco de código sincronizado).
Para dominar sincronizado, a chave é dominar o uso dessa coisa como uma trava. Para métodos não estáticos (métodos de membro) de uma classe, significa obter a trava da instância do objeto; Para métodos estáticos (métodos de classe) de uma classe, é necessário obter a trava do objeto de classe; Para blocos de código síncrono, é necessário especificar qual bloqueio do objeto é obtido. Métodos não estáticos sincronizados podem ser considerados um bloco de código sincronizado (this) {…} contendo todo o método.
Seja um bloco de código síncrono ou um método de sincronização, apenas um thread pode entrar de cada vez (no máximo um thread executa o segmento de código ao mesmo tempo.) E se outros threads tentarem entrar (seja o mesmo bloco síncrono ou um bloco de sincronização diferente), a JVM os pendurará (coloque -os no bloqueio de espera). Essa estrutura é chamada de seção crítica na teoria da simultaneidade.
Na JVM, para melhorar a eficiência, cada encadeamento em execução ao mesmo tempo terá uma cópia de cache dos dados que está processando. Quando usamos o Synchronzied para sincronização, o que é realmente sincronizado é o bloco de memória que representa o objeto bloqueado em diferentes encadeamentos (os dados de cópia permanecerão sincronizados com a memória principal. Agora sabemos por que a sincronização da palavra é usada). Simplificando, depois que o bloco de sincronização ou o método de sincronização é executado, quaisquer modificações feitas no objeto bloqueado devem ser escritas de volta na memória principal antes de liberar o bloqueio; Depois de inserir o bloco de sincronização e obter o bloqueio, os dados do objeto bloqueado são lidos na memória principal e a cópia de dados do encadeamento que mantém a trava deve ser sincronizada com a visualização de dados na memória principal.
A seguir, são apresentados exemplos específicos para ilustrar as várias situações de sincronizado.
Método de sincronização sincronizado
Primeiro, vamos dar uma olhada no exemplo do método de sincronização:
classe pública SynchronizedTest1 estende o thread {private sincronizado void testSynchronizedMethod () {for (int i = 0; i <10; i ++) {System.out.println (thread.currentThread (). getName () + "testsynChronizedMethod:" + i); tente {thread.sleep (100); } catch (interruptedException e) {e.printStackTrace (); }}} @Override public void run () {testsynchronizedMethod (); } public static void main (string [] args) {synchronizedTest1 t = new SynchronizedTest1 (); t.start (); t.TestSynchronizedMethod (); }}Executando as saídas do programa:
Principal TestSynchronizedMethod: 0 Principal TestSynchronizedMethod: 1 Principal TestSynchronizedMethod: 2 Principal TestSynchronizedMethod: 3 Principal TestSynchronizedMethod: 4 Principais testsychronizedmethod: 5 main testsynchronizedmethmod: 6 main sThetSethmethmethod: 5 main testsynchronizedmethmod: 6 main sThethodmethmethod: 5 main testsynchronizedmethmod: 6 main sThethodmethodMod: testSynchronizedMethod:9 Thread-0 testSynchronizedMethod:0 Thread-0 testSynchronizedMethod:1 Thread-0 testSynchronizedMethod:2 Thread-0 testSynchronizedMethod:3 Thread-0 testSynchronizedMethod:4 Thread-0 testSynchronizedMethod:5 Thread-0 testSynchronizedMethod:6 Thread-0 testsynchronizedmethod: 7 thread-0 testsynchronizedmethod: 8 thread-0 testsynchronizedmethod: 9
Você pode ver que o método TestSynchronizedMethod é executado de maneira síncrona entre dois threads.
Se o método principal for modificado para o seguinte, os dois threads não poderão executar síncrona, porque o monitor de sincronização dos dois threads não é o mesmo objeto e não pode desempenhar um papel síncrono.
public static void main (string [] args) {thread t = new SynchronizedTest1 (); t.start (); Thread t1 = new SynchronizedTest1 (); t1.start (); }O resultado da saída é o seguinte:
Thread-0 testSynchronizedMethod: 0 Thread-1 testSynchronizedMethod: 0 Thread-0 testSynchronizedMethod: 1 Thread-1 testSynchronizedMethod: 1 Thread-0 testsynchronizedmethodMod: 2 Thread-1 TestSychronizedMethod: 2-0 TestSythodModModMod: testSynchronizedMethod:4 Thread-1 testSynchronizedMethod:4 Thread-0 testSynchronizedMethod:5 Thread-1 testSynchronizedMethod:5 Thread-0 testSynchronizedMethod:6 Thread-1 testSynchronizedMethod:6 Thread-0 testSynchronizedMethod:7 Thread-1 testSynchronizedMethod:7 Thread-0 testsynchronizedmethod: 8 thread-1 testsynchronizedmethod: 8 thread-0 testsynchronizedmethod: 9 thread-1 testsynchronizedmethod Method: 9
Se o método principal modificado puder ser executado de maneira síncrona entre dois threads, o método TestSynchronizedMethod precisa ser declarado como um método estático, para que os monitores dos dois threads sejam o mesmo objeto (objeto de classe) e possam ser executados de forma síncrona. O código modificado se parece com o seguinte:
classe pública SynchronizedTest1 estende o thread {private Static Syncronized void testSynchronizedMethod () {for (int i = 0; i <10; i ++) {System.out.println (thread.currentThread (). getName () + "TestSynChronizedMethod:" + I); tente {thread.sleep (100); } catch (interruptedException e) {e.printStackTrace (); }}} @Override public void run () {testsynchronizedMethod (); } public static void main (string [] args) {thread t = new SynchronizedTest1 (); t.start (); Thread t1 = new SynchronizedTest1 (); t1.start (); }}O resultado da saída é o seguinte:
Thread-0 testsynchronizedmethod: 0 Thread-0 testSynchronizedMethod: 1 Thread-0 testSynchronizedMethod: 2 Thread-0 testsynchronizedMethodMod: 3 Thread-0 testsynchronizedMethodMod: 4 Thread-0 testsynchronizedMethod: 5 TestSynizedMethodod: testSynchronizedMethod:8 Thread-0 testSynchronizedMethod:9 Thread-1 testSynchronizedMethod:0 Thread-1 testSynchronizedMethod:1 Thread-1 testSynchronizedMethod:2 Thread-1 testSynchronizedMethod:3 Thread-1 testSynchronizedMethod:4 Thread-1 testSynchronizedMethod:5 Thread-1 testSynchronizedMethod:6 Thread-1 testsynchronizedmethod: 7 thread-1 testsynchronizedmethod: 8 thread-1 testsynchronizedmethod Method: 9
A situação dos blocos síncronos é semelhante ao método de sincronização, exceto que o bloco síncrono reduz a granularidade do controle de sincronização, o que pode exercer melhor a eficiência da execução paralela multitreada.
Use este objeto para controlar a sincronização entre as mesmas instâncias de objeto:
classe pública SynchronizedTest2 estende o thread {private void testsynchronizedBlock () {synchronized (this) {for (int i = 0; i <10; i ++) {System.out.println (thread.currentThread (). getName () + "" tente {thread.sleep (100); } catch (interruptedException e) {e.printStackTrace (); }}}} @Override public void run () {testsynchronizedBlock (); } public static void main (string [] args) {synchronizedTest2 t = new SynchronizedTest2 (); t.start (); t.TestSynchronizedBlock (); }}Resultado da saída:
Principal TestSynchronizedBlock: 0 Principal TestSynchronizedBlock: 1 Principal TestSynchronizedBlock: 2 Principal TestSynchronizedBlock: 3 Principal TestSynchronizedBlock: 4 Principal TestSynchronizedBlock: 5 Principal TestSynchronizedBlock: 6 Principal TestsynChronizedBlock: 7 TestSynChRONRONIZADO: 6 PrindsynChronizedBlock: 7 TestSynChRon. testSynchronizedBlock:0 Thread-0 testSynchronizedBlock:1 Thread-0 testSynchronizedBlock:2 Thread-0 testSynchronizedBlock:3 Thread-0 testSynchronizedBlock:4 Thread-0 testSynchronizedBlock:5 Thread-0 testSynchronizedBlock:6 Thread-0 testSynchronizedBlock:7 Thread-0 testSynchronizedBlock:8 Thread-0 TestSynchronizedBlock: 9
Use objetos de classe para controlar a sincronização entre diferentes instâncias:
classe pública SynchronizedTest2 estende o thread {private void testsynchronizedBlock () {Synchronized (synchronizedTest2.class) {for (int i = 0; i <10; i ++) {System.out.println (thread.currentthread (). getName () + " tente {thread.sleep (100); } catch (interruptedException e) {e.printStackTrace (); }}}} @Override public void run () {testsynchronizedBlock (); } public static void main (string [] args) {thread t = new SynchronizedTest2 (); t.start (); Thread T2 = new SynchronizedTest2 (); t2.start (); }}Resultado da saída:
Thread-0 testSynchronizedBlock:0 Thread-0 testSynchronizedBlock:1 Thread-0 testSynchronizedBlock:2 Thread-0 testSynchronizedBlock:3 Thread-0 testSynchronizedBlock:4 Thread-0 testSynchronizedBlock:5 Thread-0 testSynchronizedBlock:6 Thread-0 testSynchronizedBlock:7 Thread-0 testSynchronizedBlock:8 Thread-0 testSynchronizedBlock:9 Thread-1 testSynchronizedBlock:0 Thread-1 testSynchronizedBlock:1 Thread-1 testSynchronizedBlock:2 Thread-1 testSynchronizedBlock:3 Thread-1 testSynchronizedBlock:4 Thread-1 testSynchronizedBlock:5 Thread-1 testSynchronizedBlock:6 Thread-1 testSynchronizedBlock:7 Thread-1 TestSynchronizedBlock: 8 Thread-1 testSynchronizedBlock: 9
Ao usar a palavra -chave sincronizada para controle de sincronização, você deve entender o monitor do objeto. Somente o processo que obtém o monitor pode ser executado e todo o resto precisa ser esperado para obter o monitor. Qualquer objeto não nulo pode ser usado como um monitor de objeto. Quando o sincronizado atua em um método, a instância do objeto é bloqueada; Ao agir em um método estático, a instância do objeto é bloqueada correspondente ao objeto.
Método síncrono para dois threads para acessar um objeto ao mesmo tempo
Quando dois threads simultâneos acessam o método síncrono do mesmo objeto, apenas um thread pode ser executado. Outro thread deve esperar que o thread atual execute isso antes que possa ser executado.
classe pública twothread {public static void main (string [] args) {final twothread twothread = new twothread (); Thread t1 = new Thread (new Runnable () {public void run () {twothread.syncmethod ();}}, "a"); Thread t2 = new Thread (new Runnable () {public void run () {twothread.syncmethod ();}}, "b"); t1.start (); t2.start (); } public sincronizado void syncmethod () {for (int i = 0; i <5; i ++) {System.out.println (thread.currentThread (). getName () + ":" + i); tente {thread.sleep (500); } catch (interruptedException ie) {}}}}Resultado da saída:
A: 0a: 1a: 2a: 3a: 4b: 0b: 1b: 2b: 3b: 4
O método de sincronização de dois objetos é acessado por dois threads
Nesse caso, o sincronizado não funciona, assim como o método comum. Porque os bloqueios correspondentes são seus respectivos objetos.
public classe doisObject {public static void main (string [] args) {final doisObject Object1 = new TwoObject (); Thread t1 = new Thread (new Runnable () {public void run () {object1.syncMethod ();}}, "object1"); t1.start (); Final TwoObject Object2 = new TwoObject (); Thread t2 = novo thread (new runnable () {public void run () {public void run () {object2.syncmethod ();}}, "object2"); t2.start (); } public sincronizado void syncmethod () {for (int i = 0; i <5; i ++) {System.out.println (thread.currentThread (). getName () + ":" + i); tente {thread.sleep (500); } catch (interruptedException ie) {}}}}Uma das saídas possíveis:
Object2: 0Object1: 0Object1: 1Object2: 1Object2: 2Object1: 2Object2: 3Object1: 3Object1: 4Object2: 4
Os dois fios acessam o método estático sincronizado
Nesse caso, como a classe está bloqueada, a qualquer momento, apenas um thread pode executar o método estático.
Acessando métodos síncronos e métodos assíncronos simultaneamente quando um thread acessa um método de sincronização de um objeto, outro thread ainda pode acessar os métodos assíncronos nesse objeto.
classe pública syncandnosync {public static void main (string [] args) {final syncandnosync syncandnosync = new syncandnosync (); Thread t1 = novo thread (new Runnable () {public void run () {syncandnosync.syncmethod ();}}, "a"); t1.start (); Thread t2 = new Thread (new Runnable () {public void run () {syncandnosync.nosyncmethod ();}}, "b"); t2.start (); } public sincronizado void syncmethod () {for (int i = 0; i <5; i ++) {System.out.println (thread.currentThread (). getName () + "em syncmethod ():" + i); tente {thread.sleep (500); } Catch (interruptedException ie) {}}} public void Nosyncmethod () {for (int i = 0; i <5; i ++) {System.out.println (thread.currentThread (). getName () + "em nuncmethod ():" + i); tente {thread.sleep (500); } catch (interruptedException ie) {}}}}Uma saída possível:
B at noSyncMethod(): 0A at syncMethod(): 0B at noSyncMethod(): 1A at syncMethod(): 1B at noSyncMethod(): 2A at syncMethod(): 2B at noSyncMethod(): 3A at syncMethod(): 3A at syncMethod(): 4B at NosyncMethod (): 4
Diferentes métodos de sincronização para acessar o mesmo objeto
Quando um thread acessa o método de sincronização A de um objeto, outros threads acessam todos os outros métodos de sincronização no objeto. Como o primeiro thread obteve o bloqueio do objeto e outros threads não podem obter o bloqueio, embora esteja acessando um método diferente, ele não obtém o bloqueio e não pode acessá -lo.
classe pública twosyncmethod {public static void main (string [] args) {final twosyncmethod twosyncmethod = new twosyncmethod (); Thread t1 = novo thread (new Runnable () {public void run () {twosyncmethod.syncmethod1 ();}}, "a"); t1.start (); Thread t2 = novo thread (new runnable () {public void run () {twosyncmethod.syncmethod2 ();}}, "b"); t2.start (); } public sincronizado void syncmethod1 () {for (int i = 0; i <5; i ++) {System.out.println (thread.currentThread (). getName () + "em syncmethod1 ():" + i); tente {thread.sleep (500); } catch (interruptedException ie) {}}} public sincronizado void syncmethod2 () {for (int i = 0; i <5; i ++) {system.out.println (thread.currentThread (). getName () + "at syncmethod2 ():" "I); tente {thread.sleep (500); } catch (interruptedException ie) {}}}}Resultado da saída:
A em syncmethod1 (): 0a em syncmethod1 (): 1a em syncmethod1 (): 2a em syncmethod1 (): 3a em syncmethod1 (): 4b em syncmethod2 (): 0b em syncmethod2 (): 1b no syncmethod2 () 2B () 2B () 2B () 2Bmethod2 (): 0b em syncmethod2 (): 1b em syncmethod2 () 2B () 2B () 2B (1B). Syncmethod2 (): 4