Gostaria de saber qual deles é melhor na prática e por quê?
Descobri que Lock
e Condition
(e outras novas concurrent
classes) são apenas mais ferramentas para a caixa de ferramentas. Eu pude fazer quase tudo o que precisava com meu martelo de garra antigo (a synchronized
palavra - chave), mas era difícil de usar em algumas situações. Várias dessas situações embaraçosas se tornaram muito mais simples quando adicionei mais ferramentas à minha caixa de ferramentas: um martelo de borracha, um martelo de bola, um pé de cabra e alguns socos nas unhas. No entanto , meu antigo martelo de garra ainda vê sua parte no uso.
Eu não acho que um seja realmente "melhor" que o outro, mas cada um é mais adequado para diferentes problemas. Em poucas palavras, o modelo simples e a natureza orientada ao escopo synchronized
ajudam a me proteger de bugs no meu código, mas essas mesmas vantagens às vezes são obstáculos em cenários mais complexos. São esses cenários mais complexos que o pacote simultâneo foi criado para ajudar a resolver. Mas o uso dessas construções de nível superior requer um gerenciamento mais explícito e cuidadoso no código.
===
Eu acho que o JavaDoc faz um bom trabalho ao descrever a distinção entre Lock
e synchronized
(a ênfase é minha):
As implementações de bloqueio fornecem operações de bloqueio mais abrangentes do que as obtidas por métodos e instruções sincronizados. Eles permitem uma estrutura mais flexível , podem ter propriedades bastante diferentes e podem suportar vários objetos Condition associados .
...
O uso de métodos ou instruções sincronizados fornece acesso ao bloqueio implícito do monitor associado a cada objeto, mas força toda aquisição e liberação de bloqueio a ocorrer de maneira estruturada em bloco : quando vários bloqueios são adquiridos, eles devem ser liberados na ordem oposta ; e todos os bloqueios devem ser liberados no mesmo escopo lexical em que foram adquiridos .
Enquanto o mecanismo de escopo para métodos e instruções sincronizados facilita muito a programação com bloqueios de monitor e ajuda a evitar muitos erros comuns de programação envolvendo bloqueios, há ocasiões em que você precisa trabalhar com bloqueios de uma maneira mais flexível. Por exemplo, * * alguns algoritmos * para atravessar estruturas de dados acessadas simultaneamente requerem o uso de "entrega em mão" ou "bloqueio de cadeia" : você adquire o bloqueio do nó A, depois o nó B, libera A e adquire C, libere B e adquira D e assim por diante. As implementações da interface Lock permitem o uso de tais técnicas, permitindo que um bloqueio seja adquirido e liberado em diferentes escopos , epermitindo que vários bloqueios sejam adquiridos e liberados em qualquer ordem .
Com esse aumento da flexibilidade, vem uma responsabilidade adicional . A ausência de bloqueio estruturado em bloco remove a liberação automática de bloqueios que ocorre com métodos e instruções sincronizados. Na maioria dos casos, o seguinte idioma deve ser usado:
...
Quando o bloqueio e o desbloqueio ocorrem em escopos diferentes , deve-se tomar cuidado para garantir que todo o código que é executado enquanto o bloqueio é mantido seja protegido por tentativa-final ou tentativa-captura para garantir que a trava seja liberada quando necessário.
As implementações de bloqueio fornecem funcionalidade adicional sobre o uso de métodos e instruções sincronizados, fornecendo uma tentativa sem bloqueio de adquirir um bloqueio (tryLock ()), uma tentativa de adquirir o bloqueio que pode ser interrompido (lockInterruptibly () e uma tentativa de adquirir o bloqueio que pode atingir o tempo limite (tryLock (long, TimeUnit)).
...