Concorrência
Com a concorrência procura-se o aumento do desempenho de uma aplicação através da divisão de trabalho por várias tarefas que se executam em simultâneo, tirando partido de eventuais múltiplos processadores.Sincronização
Na partilha de dados entre tarefas é necessário garantir que os dados estão consistentes.
Por outro lado,
a presença de mecanismos de sincronização pode originar contenção
quando duas ou mais tarefas tentam aceder ao mesmo recurso em simultâneo.
Se apenas existir leitura de dados partilhados por parte das tarefas concorrentes,
estes problemas não se colocam.
Cada objeto Java tem um trinco lógico (mutex) próprio, que pode ser (implicitamente) adquirido através da primitiva synchronized.
A primitiva synchronized é aplicada a métodos do objeto e
oferece um mecanismo de exclusão mútua a um recurso partilhado,
que previne a reordenação do código pelo compilador.
Um método sincronizado adquire o trinco do objeto no início de execução e
liberta-o no fim.
O seguinte exemplo mostra uma utilização da sincronização em Java:
public class MySynchronizedCounter { private int c = 0; public synchronized void increment() { c++; } public synchronized void decrement() { c--; } public synchronized int value() { return c; } }
Os métodos increment(), decrement() e value() da classe MySynchronizedCounter estão sincronizados. Isto significa que, caso um dos 3 métodos seja invocado, o trinco do objeto é adquirido, e se houver outra tarefa a tentar aceder a qualquer um deles, ficará bloqueada à espera que o recurso/método seja libertado pela primeira invocação.
Também é possível usar o synchronized para adquirir o trinco apenas numa parte do código.
// acquire lock of object referenced by 'this' synchronized (this) { // access shared variables protected by lock } // release lock
No contexto de uma região synchronized, é também possível utilizar variáveis de condição, usando as primitivas wait, notify e notifyAll.
As coleções são objetos que armazenam vários outros objetos.
Algumas das coleções mais conhecidas no Java são:
ArrayList, LinkedList, HashMap, HashSet, TreeMap, TreeSet, etc.
Cada coleção implementa uma interface que dita o tipo de acesso esperado:
Por omissão, as coleções as coleções não são sincronizadas, pois assim conseguem melhor desempenho sequencial.
Para situações em que é necessário sincronizar os acessos às coleções,
existem versões sincronizadas que apenas permitem um acesso de cada vez.
Para construir uma coleção sincronizada usa-se um método especial da classe Collections,
que cria a nova coleção "embrulhando" uma coleção do mesmo tipo:
List synchronizedList = Collections.synchronizedList(regularList);
Posteriormente, foram acrescentadas ao Java coleções concorrentes
(Concurrent Collections no pacote java.util.concurrent)
que utilizam estruturas de dados sofisticadas, desenhadas de raíz para garantir a consistência
da coleção mesmo quando esta é acedida concorrentemente por muitas tarefas.
Exemplos destas coleções concorrentes são:
ConcurrentHashMap, CopyOnWriteArrayList, e CopyOnWriteHashSet.
No exemplo seguinte constroí-se um mapa optimizado para acessos concorrentes:
Map<String,Object> map = new ConcurrentHashMap<>();
© Docentes de Sistemas Distribuídos,
Dep. Eng. Informática,
Técnico Lisboa