Perigos da Sincronização Excessiva:
- Redução de desempenho.
- Conflitos.
- Comportamento não determinístico.
Evitar Transferência de Controle:
- Não invocar métodos desconhecidos (projetados para serem sobrescritos ou fornecidos pelo cliente) dentro de blocos sincronizados.
- Chamadas desconhecidas podem causar:
- Exceções.
- Deadlocks.
- Corrupção de dados.
Exemplo: Classe ObservableSet:
- Implementa um padrão de observador, notificando sobre elementos adicionados ao conjunto.
- Problema: Invocar métodos de observadores dentro de blocos sincronizados causa exceções (ex.: ConcurrentModificationException) e deadlocks.
Problemas e Soluções:
- Exceções:
- Ocorrência: Modificar a lista de observadores enquanto itera sobre ela.
- Solução: Deslocar invocações para fora dos blocos sincronizados.
Deadlocks:
Ocorrência: Threads bloqueiam recursos mutuamente.
Solução: Evitar bloqueios reentrantes desnecessários.
Uso de Coleções Concorretes:
- Substituir listas sincronizadas por CopyOnWriteArrayList.
- Evita bloqueios durante iterações, ideal para listas raramente modificadas.
Boas Práticas:
- Minimize operações em blocos sincronizados.
- Faça apenas o essencial (examine/modifique dados compartilhados).
- Realize operações demoradas fora dos blocos sincronizados.
Impacto no Desempenho:
- Contenção reduz oportunidades de paralelismo.
- Limita otimizações da JVM.
Abordagens para Sincronização:
- Omita sincronização interna e permita que o cliente sincronize.
- Sincronize internamente apenas se conseguir alta concorrência.
- Exemplo: StringBuilder (não sincronizado) substituiu StringBuffer (sincronizado).
Sincronização de Campos Estáticos:
- Sincronize modificações internas se métodos puderem ser chamados por várias threads.
- Campos estáticos compartilham estado global.
Conclusão:
- Evite deadlocks e falhas de segurança deslocando chamadas desconhecidas para fora de blocos sincronizados.
- Documente explicitamente a thread-safety da classe.
Exemplos de Código
1. Classe ObservableSet com Problema de Exceção
public class ObservableSet<E> {
private final Set<E> set = new HashSet<>();
private final List<SetObserver<E>> observers = new ArrayList<>();
public synchronized void addObserver(SetObserver<E> observer) {
observers.add(observer);
}
public synchronized void removeObserver(SetObserver<E> observer) {
observers.remove(observer);
}
public synchronized boolean add(E element) {
boolean added = set.add(element);
if (added) notifyElementAdded(element);
return added;
}
private void notifyElementAdded(E element) {
for (SetObserver<E> observer : observers) {
observer.added(this, element);
}
}
}
2. Corrigindo o Problema com CopyOnWriteArrayList
import java.util.concurrent.CopyOnWriteArrayList;
public class ObservableSet<E> {
private final Set<E> set = new HashSet<>();
private final CopyOnWriteArrayList<SetObserver<E>> observers = new CopyOnWriteArrayList<>();
public void addObserver(SetObserver<E> observer) {
observers.add(observer);
}
public void removeObserver(SetObserver<E> observer) {
observers.remove(observer);
}
public boolean add(E element) {
boolean added = set.add(element);
if (added) notifyElementAdded(element);
return added;
}
private void notifyElementAdded(E element) {
for (SetObserver<E> observer : observers) {
observer.added(this, element);
}
}
}
3. Exemplo de Deadlock (a ser evitado)
executor.execute(() -> {
s.removeObserver(this); // Deadlock ao tentar remover durante bloqueio
});
4. Boas Práticas para Sincronização
public synchronized void performOperation() {
// Operação essencial dentro do bloco sincronizado
sharedData.modify();
// Operação demorada fora do bloco sincronizado
processUnsharedData();
}
exemplos do livro
Top comments (0)