Hashtable - O que significa ser sincronizado?

Olá pessoal, gostaria de saber o que significa ser sincronizado, estou estudando sobre Hashtable e ele é sincronizado.
Sincronizado seria uma estrutura de dados que pode ser acessada simultaneamente por vários Threads?
Eu dei uma lida sobre, mas ainda não ficou bem claro pra mim.
vlw

Justamente ao contrário. É uma estrutura que não pode ser acessado simultaneamente por duas Threads.

Agora, o problema do sincronismo é que, normalmente, HashTables não são usados em contextos multi-thread. Nem HashTable e nem list. E, para garantir que existe sincronização, há todo um processamento adicional, tanto por parte do Java, quanto do SO. Por isso, HashTables tem pior performance que os maps, na maior parte dos casos.

Entendi. A vantagem disso seria por exemplo, quando alguém remover um elemento da Hashtable e outro tentasse buscar esse elemento tudo ao mesmo tempo, ele não econtraria este elemento, porque o método de remover foi executado primeiro… certo?
A vantagem disto é para não ter este tipo de problema? Porque pode ser que duas threads acesse essa estrutura ao mesmo tempo e gere algum problema, certo?
O HashMap por exemplo, não é sincronizado, então para uma aplicação multi-thread, seria recomendável eu sincronizá-lo?
vlw

Isso mesmo.

Para uma aplicação multi-thread, desde que as threads acessem o mapa ao mesmo tempo, seria recomendável usar o HashTable no lugar do HashMap, ou sincronizar o mapa usando:

Map<Tipo, Tipo> novoMapa = Collections.synchronizedMap(mapaNaoSincronizado);

Entretanto, como eu falei, essa situação é bastante rara. Geralmente os maps e listas serão usados dentro de métodos sincronizados, o que elimina a necessidade deles mesmos o serem. E, caso você realmente precise de acesso concorrente, existem estruturas melhores no pacote java.util.concurrent.

Entendi. Bacana…
Bom, sua resposta me clareou muito bem. Não estou querendo ser muito pedinte, mas se você puder, queria que me tirasse outra dúvida relacionado a Hashtable…
Bom, pelo que eu entendi, o Hashtable é uma tabela de hash, que armazena os códigos de hash gerados a partir da chave, e esses códigos são armazenados em uma sequencia, e se vc não definir a capacidade inicial da hashtable, toda vez que vc insere um novo chave/valor, ela faz um rehash para aumentar seu tamanho? E se os hash se colidirem, ela armazena na mesma tabela do outro?

Por padrão, caso você não diga a capacidade, a HashTable inicia com capacidade 11 e load factor de 0.75.

O que é um hashTable, exatamente? Nada mais é do que um vetor, contendo listas de objetos. Quando você armazena um objeto na hashTable, o que o Java faz é:

  1. Obtém o hash do objeto;
  2. Faz hash % tamanho do vetor, isso dá um índice entre 0 e o tamanho do vetor;
  3. Vai até aquela posição do vetor. E obtém a lista correspondente.
  4. Se ela estiver vazia, põe o objeto como primeiro da lista.
  5. Se não estiver vazia, testa com equals objeto por objeto.
  6. Caso ache um igual, substitui o objeto pelo encontrado. Caso não ache, armazena ao final da lista.

Agora, a cada objeto inserido, a HashTable mantém um contador. Quando o número de objetos é maior do que a HashTable em “load factor”%, há um rehash. Então, por exemplo, uma HashTable criada sem construtores irá crescer caso seu tamanho exceda 11 + 75%*11 = 11 + 8 = 19 objetos.

O java não deixa claro o quanto a HashTable cresce, mas já li por aí (embora não tenha conferido nos fontes) que é 50% a mais do que a capacidade máxima atual. No caso, ela passaria de 19 elementos para caber 28. O rehash é uma operação cara, pois não só há alocação de memória para os novos elementos, como todos os elementos devem ser copiados para a nova estrutura e a estrutura antiga é apagada. Entretanto, nunca vi isso chegar a ser um grave problema de performance (geralmente o gargalo está no acesso, e não na criação da HashTable).

Note que, dada a essa política de crescimento, a HashTable só fará novo rehash após ter 28+75%*28=28+21=50 objetos.

O load factor faz o balanço entre consumo de memória, tempo de acesso e quantidade de rehashs. Quanto maior o load factor, mais tempo leva para um rehash, menos memória o table ocupa, porém, menor o tempo de acesso médio por objeto (já que o hashTable terá listas maiores). Um load factor pequeno implica em pouco tempo de acesso, muita memória ocupada (já que há o overhead de ter mais listas com poucos objetos cada) e muitos rehashs. O valor 0.75 foi escolhido como padrão pois, segundo a Sun, é uma boa solução intermediária e representa um bom compromisso médio entre performance e memória consumida.

De maneira geral, você pode usar um Hash ou um Map imaginando que a performance para acessar um elemento é próxima de O(1). Ou seja, instantâneo para a maior parte das aplicações.

Note também que para a HashTable funcionar bem, é importante que tanto o método equals, quanto o método hashcode do objeto estejam corretamente implementados e sejam otimizados. Também é importante que o objeto não altere seu HashCode enquanto estiver dentro da tabela hash, ou ele pode não ser mais encontrado.

O HashMap segue a mesma linha, mas como não possui sincronização, tende a fazer tudo com maior performance. Desde o Java 1.2, não há mais motivos para usar o HashTable ou o Vector. Note que após o Java 1.2, a Collections Framework também introduziu o TreeMap, que usa uma árvore binária balanceada por baixo dos panos. Ele também mantém suas chaves ordenadas. Além dele, há também o LinkedHashMap, que usa o HashMap e uma LinkedList para garantir que as chaves se manterão em ordem de inserção.

O HashMap e o HashTable não dão qualquer garantia sobre a ordenação dos elementos. Se você olhar, o algorítmo do resto sobre o hash realmente irá bagunçar tudo. :slight_smile:

ViniGodoy, cara muito obrigado, sua contribuição foi de extrema importância. Entendi tudo claramente agora, você explicou de maneira simples e objetiva, valeu mesmo cara…
Eu to estudando Hashtable, só para um trabalho acadêmico mesmo, não pretendo usar mesmo… hahaha
Valeu mesmo :!: :!: :!: