Eliminar a dependência entre threads
- Como cada thread tem sua própria porção da Stack, e ela é invisível para outras threads, valores armazenados na Stack são thread-safe
- A memória Heap é compartilhada entre threads; por isso apenas objetos imutáveis são thread-safe, objetos mutáveis são thread-unsafe
- objetos mutáveis podem ser alterados por uma thread sem que outra perceba, e essa acaba por utilizar um valor incorreto
- com objetos mutáveis pode ocorrer do compilador utilizar um valor em cache não percebendo que o cache é inválido, devido a alterações feitas em outras threads
- É possível desabilitar a optimização de cache local de um valor de Heap por uma thread, forçando sempre a leitura da memória principal; utiliza-se a palavra reservada
volatile
Ações atômicas não bloqueantes
- Uma ação atômica é uma ação que é garantida de ser executada em uma thread sem interrupção;
- operações que a CPU consegue executar em um único ciclo são atómicas, por definição
- atribuição de variáveis são ações atômicas, exceto para os tipos
long
edouble
— são valores de 64 bits que levariam duas operações em uma plataforma 32 bits - operações como
+ - / * % ++ —
não são atômicas
- O pacote
java.util.concurrent.atomic
dispõe classes que implementam comportamentos atômicos em variáveis únicas de forma thread-safe e prevenindo lock's. - algumas delas são
AtomicBoolean
,AtomicInteger
,AtomicLong
,AtomicReference<V>
- as classes desse pacote fazem as alterações não no objeto da memória principal, mas no cópia do objeto que está em cache; isso permite que, ao atualizar a memória principal, a operação pareça atômica
- variáveis atômicas também se comportam como variáveis
volatile
Travas intrínsecas
Bloqueia acesso a um objeto compartilhado, por outros objetos
- São feitos através de blocos de código
synchornized
- Garante a ordem de execução e um objeto consistente
- Lógica síncrona pode causar um gargalo de performance e escalabilidade, em aplicações multi-thread
- A classe
Collections
dispõe wrappers síncronos para objetos de coleção comoCollection
,List
,Set
eMap
; com eles a manipulação de itens (através de métodos da própria instância) a coleção não precisa ser feita em um blocosynchronized
- a leitura de uma lista síncrona deve ser feita dentro de um bloco
synchronized
, caso contrário pode ocorrer de uma thread lê-la enquanto outra thread adiciona um item, podendo resultar em inconsistência nos dados

Automação de concorrência não-bloqueante
Através de wrappers de coleções como
CopyOnWriteArrayList
e CopyOnWriteArraySet
, é possível utilizar listas de forma segura sem a necessidade de um bloco síncrono. Isso ocorrer pois sempre que uma operação de mutação é feita sobre esses wrappers, eles criam uma cópia da coleção e aplicam a alteração nessa cópia.Por ser uma operação muito custosa, é uma abordagem utilizada apenas em coleções pequenas em que sejam feitas poucas operações de mutação

Mecanismos alternativos para travas
- O pacote
java.util.concurrent.locks
dispõe diversas opções para abordar a lógica de travas

ReentrantReadWriteLock
para implementar um bloco de código síncrono