0. Em relação à sincronização do encadeamento (1) Por que precisamos sincronizar o Multithreading?
A sincronização de threads refere -se a permitir que vários threads em execução cooperem bem juntos para permitir que vários threads ocupem razoavelmente e liberem recursos conforme necessário. Utilizamos blocos de código de sincronização e métodos de sincronização em Java para atingir esse objetivo. Por exemplo, resolva o problema da execução de pedidos não fixados com vários threades:
classe pública twothreadtest {public static void main (string [] args) {thread th1 = new mythread1 (); Thread Th2 = novo mythread2 (); Th1.start (); th2.start (); }} classe mythread2 estende o thread {@Override public void run () {for (int i = 0; i <10; i ++) sistema. out.println ("Thread 1 contador:"+i); }} classe mythread1 estende o thread {@Override public void run () {for (int i = 0; i <10; i ++) sistema. out.println ("Thread 1 contador:"+i); }} classe mythread1 estende o thread {@Override public void run () {for (int i = 0; i <10; i ++) sistema. out.println ("contador de thread 2:"+i); }}O resultado da execução multithread nesse estado é inserir aleatoriamente a execução à vontade, que depende inteiramente do agendamento de threads da JVM. Em muitos casos em que a execução ordenada é necessária, esse estado de execução aleatório é obviamente inadequado.
public class ThreadTest {public static void main (string [] args) {mythread thread = new mythread (); Thread th1 = novo thread (thread); Thread th2 = novo thread (thread); Th1.start (); th2.start (); }} classe mythread implementa runnable {@Override public sincronizado void run () {for (int i = 0; i <10; i ++) sistema. out.println (Thread. currentThread (). getName ()+"contador:"+i); }}Depois de usar o método de sincronização, podemos controlar o encadeamento para ocupar exclusivamente o objeto do corpo de execução. Dessa forma, durante o processo de execução, o encadeamento pode executar as tarefas no corpo de execução ao mesmo tempo e sair do estado de bloqueio. A JVM despacha outro thread para executar as tarefas no corpo de execução ao mesmo tempo.
(2) O paradigma para criação e corrida de threads:
No passado, também tínhamos nosso próprio paradigma de programação para criação e corrida de threads. Geralmente, definimos uma classe de execução para reescrever o método run (), mas esse método coloca o órgão de execução e as tarefas executadas, o que não é propício para a dissociação da perspectiva da engenharia de software. A execução de um encadeamento significa que um encadeamento executa uma tarefa de um objeto através do objeto de execução. Nessa perspectiva, separar o prescritor da tarefa da classe de execução pode deixar claro os vários papéis da programação multithread e, assim, obter uma boa dissociação. A seguir, é apresentado o paradigma de programação para criação e execução de threads:
classe pública formalthreadclass {public static void main (string [] args) {thread thread = new Thread (new MyRunnable ()); thread.start (); }} classe MyRunnable implementa Runnable {MyTask mytask = new MyTask (); @Override public void run () {mytask.dotask (); }} classe myTask {public void Dotask () {System. out.println ("Isso é verdadeiro"); }}
1. Princípio sincronizado
Em Java, cada objeto possui e possui apenas um bloqueio de sincronização. Isso também significa que o bloqueio de sincronização existe no objeto.
Quando chamamos o método sincronizado de um objeto, adquirimos o bloqueio de sincronização do objeto. Por exemplo, o sincronizado (OBJ) adquire o bloqueio de sincronização do "objet Obj".
O acesso a bloqueios de sincronização por diferentes encadeamentos é mutuamente exclusivo. Em outras palavras, em um determinado momento, a trava de sincronização do objeto só pode ser obtida por um thread! Através de bloqueios de sincronização, podemos obter acesso mutuamente exclusivo a "objetos/métodos" em vários threads. Por exemplo, agora existem dois threads a e threads B, que acessam a "trava síncrona do objeto Obj". Suponha que em algum momento a Thread A adquira o "Lock de sincronização do OBJ" e execute algumas operações; No momento, o Thread B também tenta adquirir o "Lock de sincronização do OBJ" - o Thread B falhará em adquirir, ele deve esperar até que o Thread A libera o "Lock de sincronização do OBJ" e só possa ser executado.
2. Regras básicas sincronizadas
Resumimos as regras básicas de sincronizadas nos 3 seguintes e as ilustramos através de exemplos.
Artigo 1: Quando um thread acessa o "método sincronizado" ou "bloco de código sincronizado" de "um determinado objeto", outros threads serão bloqueados do acesso ao "método sincronizado" ou "bloco de código sincronizado" de "o objeto".
Artigo 2: Quando um thread acessa o "método sincronizado" ou "bloco de código sincronizado" de "um determinado objeto", outros threads ainda podem acessar o bloco de código assincronizado de "este objeto".
Artigo 3: Quando um thread acessa o "método sincronizado" ou "bloco de código sincronizado" de "um determinado objeto", outros threads serão impedidos de acessar outros "métodos sincronizados" ou "bloco de código sincronizado" de "o objeto".
(1) Artigo 1:
Quando um thread acessa o "método sincronizado" ou "bloco de código sincronizado" de "um determinado objeto", outros threads serão bloqueados do acesso ao "método sincronizado" ou "bloco de código sincronizado" de "o objeto". Abaixo está o programa de demonstração correspondente ao "bloco de código sincronizado".
classe MyRunable implementa runnable {@Override public void run () {synchronized (this) {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100ms System.out.println (Thread.currentThread (). GetName () + "loop" + i); }} catch (interruptedException ie) {}}}} public class Demo1_1 {public static void main (string [] args) {runnable demo = new MyRunable (); // Crie um novo thread "Runnable Object" T1 = novo thread (demonstração, "T1"); // Crie um novo "Thread T1", T1 é baseado no thread de objeto Runnable T2 = novo Thread (Demo, "T2"); // Crie um novo "Thread T2", T2 é baseado no objeto Runnable T1.start (); // Iniciar "Thread T1" T2.Start (); // Iniciar "Thread T2"}} Resultados em execução:
loop t1 loop 0t1 loop 1t1 loop 2t1 loop 3t1 loop 4t2 loop 0t2 loop 1t2 loop 2t2 loop 3t2 loop 4
O resultado mostra que existe um "bloco de código sincronizado (este) no método run (), e T1 e T2 são threads criados com base no objeto Runnable" Demo ". Isso significa que podemos considerar isso em sincronizado (isso) como "objeto de demonstração executável"; Portanto, os threads T1 e T2 compartilham "trava síncrona do objeto de demonstração". Portanto, quando um thread está em execução, outro thread deve aguardar o "Thread em execução" para liberar o "Lock de sincronização de demonstração" antes que possa ser executado.
Se você confirmar, você descobriu esse problema. Em seguida, modificamos o código acima e o executamos para ver como é o resultado e ver se você ficará confuso. O código -fonte modificado é o seguinte:
classe mythread estende thread {public mythread (nome da string) {super (nome); } @Override public void run () {sincronizado (this) {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100ms System.out.println (Thread.currentThread (). GetName () + "loop" + i); }} catch (interruptedException ie) {}}}} public class Demo1_2 {public static void main (string [] args) {thread t1 = new mythread ("t1"); // Crie novo thread "Thread T1" T2 = novo mythread ("T2"); // Crie novo "Thread T2" t1.start (); // Iniciar "Thread T1" T2.Start (); // Iniciar "Thread T2"}} Descrição do código: Comparando Demo1_2 e Demo1_1, descobrimos que a classe Mythread em Demo1_2 é diretamente herdada do Thread e T1 e T2 são threads infantis mythread.
Felizmente, o método "run () de Demo1_2" também chamado sincronizado (this), assim como o método "run () de Demo1_1" também chamado de sincronizado (isto)!
Então, o processo de execução do Demo1_2 é o mesmo que Demo1_1? Resultados em execução:
T1 loop 0t2 loop 0t1 loop 1t2 loop 1t1 loop 2t2 loop 2t1 loop 3t2 loop 3t1 loop 4t2 loop 4
Resultados Descrição:
Se esse resultado não o surpreende, acredito que você tem um entendimento mais profundo de sincronizado e isso. Caso contrário, continue lendo a análise aqui.
Isso em sincronizado (isso) refere -se ao "objeto de classe atual", ou seja, o objeto atual correspondente à classe onde está localizado (este). Seu objetivo é obter a "trava síncrona do objeto atual".
Para Demo1_2, isso em sincronizado (isso) representa o objeto Mythread, enquanto T1 e T2 são dois objetos misithread diferentes. Portanto, quando T1 e T2 executam sincronizados (isso), eles adquirem os bloqueios de sincronização de diferentes objetos. Para o par Demo1_1, este em sincronizado (isso) representa o objeto inútil; T1 e T2 compartilham um objeto inútil. Portanto, um thread adquire o bloqueio de sincronização do objeto, que fará com que outro thread aguarde.
(2) Artigo 2:
Quando um thread acessa o "método sincronizado" ou "bloco de código sincronizado" de "um determinado objeto", outros threads ainda podem acessar o bloco de código assíncronizado de "este objeto".
Abaixo está o programa de demonstração correspondente ao "bloco de código sincronizado".
classe contagem {// métodos contendo bloqueios de sincronização sincronizados public void synMethod () {sincronizado (this) {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // durma para 100ms System.out.println (Thread.currentThread (). GetName () + "synmethod loop" + i); }} catch (interruptEdException ie) {}}} // método assíncrono public void nonsynmethod () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); System.out.println (thread.currentThread (). GetName () + "nOnsynMethod loop" + i); }} catch (interruptEdException ie) {}}} public class Demo2 {public static void main (string [] args) {contagem final contagem = new count (); // Criar novo T1, T1 chamará o método synMethod () de "contagem objeto" thread t1 = new Thread (new runnable () {@Override public void run () {count.synmethod ();}}, "t1"); // Crie um novo T2, T2 chamará o método NonsynMethod () do "contagem objeto" Thread T2 = new Thread (new Runnable () {@Override public void run () {count.NonsynMethod ();}}, "T2"); t1.start (); // Iniciar t1 t2.start (); // Iniciar T2}} Resultados em execução:
t1 synMethod loop 0t2 nonSynMethod loop 0t1 synMethod loop 1t2 nonSynMethod loop 1t1 synMethod loop 2t2 nonSynMethod loop 2t1 synMethod loop 3t2 nonSynMethod loop 3t1 synMethod loop 4t2 nonSynMethod loop 4
Resultados Descrição:
Dois novos threads infantis T1 e T2 são criados no encadeamento principal. T1 chamará o método synMethod () do objeto de contagem, que contém blocos de sincronização; O T2 chamará o método não sinMethod () do objeto de contagem, que não é um método de sincronização. Quando o T1 está em execução, embora sincronizado (isso) seja chamado para obter o "Lock de sincronização da contagem"; Não causa o bloqueio do T2 porque o T2 não usa o bloqueio de sincronização "contagem".
(3) Artigo 3:
Quando um thread acessa o "método sincronizado" ou "bloco de código sincronizado" de "um determinado objeto", outros encadeamentos acessam a outros "métodos sincronizados" ou "bloco de código sincronizado" de "o objeto" será bloqueado.
Também modificaremos o corpo do método NonsynMethod () no exemplo acima com sincronizado (isso). O código -fonte modificado é o seguinte:
classe contagem {// métodos contendo bloqueios de sincronização sincronizados public void synMethod () {sincronizado (this) {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // durma para 100ms System.out.println (Thread.currentThread (). GetName () + "synmethod loop" + i); }} catch (interruptEdException ie) {}}} // métodos contendo a sincronização sincronizada bloqueia public void nonsynMethod () {synchronized (this) {try {for (int i = i <5; i ++) {thread.Sleep (100); System.out.println (thread.currentThread (). GetName () + "nOnsynMethod loop" + i); }} catch (interruptedException ie) {}}}} public class Demo3 {public static void main (string [] args) {contagem final contagem = new count (); // Criar t1, t1 chamará o método synMethod () do thread "contagem objeto" t1 = new Thread (new Runnable () {@Override public void run () {count.syncmethod ();}}, "t1"); // Crie novo T2, T2 chamará o método nãonMethod () do "contagem objeto" Thread t2 = new Thread (new Runnable () {@Override public void run () {count.NonsynMethod ();}}, "T2"); t1.start (); // Iniciar t1 t2.start (); // Iniciar T2}} Resultados em execução:
t1 synMethod loop 0t1 synMethod loop 1t1 synMethod loop 2t1 synMethod loop 3t1 synMethod loop 4t2 nonSynMethod loop 0t2 nonSynMethod loop 1t2 nonSynMethod loop 2t2 nonSynMethod loop 3t2 nonSynMethod loop 4
Resultados Descrição:
Dois novos threads infantis T1 e T2 são criados no encadeamento principal. Tanto o T1 e o T2 chamam sincronizados (este), que é um objeto de contagem (contagem), e a contagem de compartilhamentos T1 e T2. Portanto, quando o T1 estiver em execução, o T2 será bloqueado e o T1 será executado para liberar a "trava síncrona do objeto de contagem" antes que o T2 possa ser executado.
3. Método sincronizado e bloco de código sincronizado
O "método sincronizado" usa o método de modificação sincronizado, enquanto o "bloco de código sincronizado" usa bloco de código de modificação sincronizado.
Exemplo de método sincronizado
public sincronizado void foo1 () {System.out.println ("Método sincronizado");} Código sincronizado bloco public void foo2 () {sincronizado (this) {System.out.println ("Método sincronizado"); }} Isso no bloco de código sincronizado refere -se ao objeto atual. Isso também pode ser substituído por outros objetos, como esse é substituído pelo OBJ, o Foo2 () adquire o bloqueio de sincronização do OBJ quando sincronizado (OBJ).
Os blocos de código sincronizados podem controlar as áreas de acesso restrito a conflitos com mais precisão e, às vezes, com mais eficiência. Aqui está um exemplo para demonstrar:
// Demo4.Java Código fonte public Demo4 {public sincronizado void synMethod () {for (int i = 0; i <1000000; i ++); } public void synblock () {sincronizado (this) {for (int i = 0; i <1000000; i ++); }} public static void main (string [] args) {Demo4 Demo = new Demo4 (); Long Start, diff; start = system.currenttimemillis (); // Obtenha a hora atual (Millis) Demo.SyncMethod (); // CHAMADO "Método sincronizado" diff = System.CurrentTimEmillis () - Start; // obtém "diferença de tempo" System.out.println ("syncmethod ():"+ diff); start = system.currenttimemillis (); // Obtenha a hora atual (Millis) Demo.syncBlock (); // CHAMADO "Método sincronizado bloco" diff = System.currenttimemillis () - start; // obtém "diferença de tempo" System.out.println ("SyncBlock ():"+ diff); }} (Uma vez) Resultado da execução:
synMethod (): 11SynBlock (): 3
4. Lock de instância e bloqueio global
Bloqueio de instância-bloqueado em um objeto de instância. Se a aula é um singleton, a fechadura também tem o conceito de uma trava global.
(1) A palavra -chave sincronizada corresponde ao bloqueio da instância.
(2) Bloqueio global-o bloqueio é direcionado para uma aula. Não importa quantos objetos seja a instância, os threads compartilham o bloqueio.
O bloqueio global corresponde a sincronizado estático (ou bloqueado no objeto de classe ou carregador de classe desta classe).
Há um exemplo muito vívido de "Bloqueio de instância" e "Global Lock":
classe pulbic algo {public sincronizado void ISSynca () {} public sincronizado void ISSYNCB () {} public Static Sincronized void csyncA () {} public estático vazio sincronizado csyncb () {}}}}} Suponha que algo tenha duas instâncias x e y. Analise os bloqueios adquiridos pelos quatro conjuntos de expressões a seguir.
(1) x.issynca () e x.issyncb ()
(2) x.issynca () e y.issynca ()
(3) x.csynca () e y.csyncb ()
(4) x.issynca () e algo.csynca ()
(1) não pode ser acessado simultaneamente.
Porque ISSynca () e ISSyncb () são bloqueios de sincronização que acessam o mesmo objeto (objeto x)!
// Locktest1.java Código -fonte da classe algo {public sincronizado void ISSynca () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100ms System.out.println (Thread.currentThread (). GetName ()+": ISSynca"); }} catch (interruptEdException ie) {}} public sincronizado void ISSyncb () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Durma para 100ms System.out.println (Thread.currentThread (). GetName ()+": ISSyncb"); }} catch (interruptEdException ie) {}}} public class Locktest1 {algo x = new Something (); Algo y = novo algo (); // Compare (01) x.issynca () com x.issyncb () private void test1 () {// Crie o novo T11, T11 chamará x.issynca () Thread t11 = new Thread (new Runnable () {@Override public void Run (X.issynca ();}} "". // Crie novo T12, T12 chamará x.issyncb () thread thread t12 = novo thread (new runnable () {@Override public void run () {x.issyncb ();}}}, "t12"); t11.start (); // Iniciar T11 T12.Start (); // Iniciar t12} public static void main (string [] args) {Locktest1 demo = new LockTest1 (); Demo.test1 (); }} Resultados em execução:
T11: ISSYNCAT11: ISSYNCAT11: ISSYNCAT11: ISSYNCAT12: ISSYNCBT12: ISSYNCBT12: ISSYNCBT12: ISSYNCBT12: ISSYNCBT12: ISSYNCBT12: ISSYNCBT12: ISSYNCT12: ISSYNCB:
(2) pode ser acessado ao mesmo tempo
Como não está acessando o bloqueio de sincronização do mesmo objeto, x.issynca () acessa a trava de sincronização de x, enquanto y.issynca () acessa a trava de sincronização de y.
// Locktest2.Java Código -fonte da classe algo {public sincronizado void ISSynca () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100ms System.out.println (Thread.currentThread (). GetName ()+": ISSynca"); }} catch (interruptEdException ie) {}} public sincronizado void ISSyncb () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100ms System.out.println (Thread.currentThread (). GetName ()+": ISSyncb"); }} catch (interruptedException ie) {}} public static sincronizado void csynca () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100ms System.out.println (Thread.currentThread (). GetName ()+": csynca"); }} catch (interruptedException ie) {}} public static sincronizado void csyncb () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100ms System.out.println (Thread.currentThread (). GetName ()+": csyncb"); }} catch (interruptEdException ie) {}}} public class Locktest2 {something x = new Something (); Algo y = novo algo (); // Compare (02) x.issynca () com y.issynca () private void test2 () {// Criar novo T21, T21 chamará x.issynca () Thread T21 = new Thread (new Runnable () {@Override public void Run () {x.iSyCA ();}}}}; // Crie novo T22, T22 chamará x.issyncb () Thread T22 = new Thread (new Runnable () {@Override public void run () {y.issynca ();}}, "t22"); t21.start (); // Iniciar T21 T22.Start (); // Iniciar t22} public static void main (string [] args) {Locktest2 demo = new LockTest2 (); Demo.test2 (); }} Resultados em execução:
T21: ISSYNCAT22: ISSYNCAT21: ISSYNCAT22: ISSYNCAT21: ISSYNCAT22: ISSYNCAT21: ISSYNCAT22: ISSYNCAT21: ISSYNCAT22: ISSYNCAT21: ISSYNCAT22: ISSYNCAT21:
(3) não pode ser acessado simultaneamente
Como a csynca () e o csyncb () são tipos estáticos, x.csynca () é equivalente a algo.issynca () e y.csyncb () são equivalentes a algo.ISSYNCB (), eles compartilham uma trava de sincronização e não podem ser perguntados na mesma época.
// Locktest3.Java Código -fonte da classe algo {public sincronizado void ISSynca () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100ms System.out.println (Thread.currentThread (). GetName ()+": ISSynca"); }} catch (interruptEdException ie) {}} public sincronizado void ISSyncb () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100ms System.out.println (Thread.currentThread (). GetName ()+": ISSyncb"); }} catch (interruptedException ie) {}} public static sincronizado void csynca () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100ms System.out.println (Thread.currentThread (). GetName ()+": csynca"); }} catch (interruptedException ie) {}} public static sincronizado void csyncb () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100ms System.out.println (Thread.currentThread (). GetName ()+": csyncb"); }} catch (interruptEdException ie) {}}} public class Locktest3 {something x = new Something (); Algo y = novo algo (); // compare (03) x.csyncA () com y.csyncb () private void test3 () {// criar novo t31, t31 ligará para x.issynca () thread t31 = new thread (new runnable () {@override public void run () {x.csynCA (); // Criar novo T32, T32 chamará x.issyncb () Thread t32 = new Thread (new runnable () {@Override public void run () {y.csyncb ();}}, "t32"); t31.start (); // Iniciar t31 t32.start (); // Iniciar t32} public static void main (string [] args) {Locktest3 Demo = new LockTest3 (); Demo.test3 (); }} Resultados em execução:
t31: cSyncAt31: cSyncAt31: cSyncAt31: cSyncAt31: cSyncAt32: cSyncBt32: cSyncBt32: cSyncBt32: cSyncBt32: cSyncBt32: cSyncBt32: cSyncBt32: cSyncBt32: cSyncBt32: csyncb
(4) pode ser acessado simultaneamente
Como o ISSYNCA () é um método de instância, o x.issynca () usa o bloqueio do objeto X; Enquanto o csynca () é um método estático, algo.csynca () pode entender que é um "bloqueio de classe" usado. Portanto, eles podem ser acessados simultaneamente.
// Locktest4.Java Código -fonte da classe algo {public sincronizado void ISSynca () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100ms System.out.println (Thread.currentThread (). GetName ()+": ISSynca"); }} catch (interruptEdException ie) {}} public sincronizado void ISSyncb () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100ms System.out.println (Thread.currentThread (). GetName ()+": ISSyncb"); }} catch (interruptedException ie) {}} public static sincronizado void csynca () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // Sleep 100ms System.out.println (Thread.currentThread (). GetName ()+": csynca"); }} catch (interruptedException ie) {}} public static sincronizado void csyncb () {try {for (int i = 0; i <5; i ++) {thread.sleep (100); // durma para 100ms System.out.println (thread.currentThread (). GetName ()+": csyncb"); }} catch (interruptEdException ie) {}}} public class Locktest4 {something x = new Something (); Algo y = novo algo (); // compare (04) x.issynca () com algo.csynca () private void test4 () {// Criar novo T41, T41 ligará para x.issynca () Thread t41 = new Thread (new Runnable () {@Override public run () {x.synca (); // Crie novo T42, T42 ligará para x.issyncb () thread t42 = new Thread (new runnable () {@Override public void run () {something.csynca ();}}, "t42"); t41.start (); // Iniciar t41 t42.start (); // Iniciar t42} public static void main (string [] args) {Locktest4 Demo = new LockTest4 (); Demo.test4 (); }} Resultados em execução:
T41: ISSYNCAT42: CSYNCAT41: ISSYNCAT42: CSYNCAT41: ISSYNCAT42: CSYNCAT41: ISSYNCAT42: CSYNCAT41: ISSYNCAT42: CSYNCAT41: ISSYNCAT42: CSYNCAT41: