concurrentHashMap是java 线程安全全且强一致性的吗

有比ConcurrentHashMap线性安全更安全的HashMap吗_java吧_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0成为超级会员,使用一键签到本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:225,035贴子:
有比ConcurrentHashMap线性安全更安全的HashMap吗收藏
可能我的线程并发太高了,还是不知道怎么回事,用ConcurrentHashMap还是会get到null出来,然后再get一两次就出来一个不是null的正常数据,有人给我指条明路吗。
你想get也同步?
是null 是null 就是null
ConcurrentHashMap 是不接受 null 的,get 出 null 意味著已经被 delete 了。如果你认为这种事不该发生,那就是理解错 ConcurrentHashMap 所保障的安全了
使用线程安全的类写写出的代码不一定就线程安全,如果你不能确定自己代码是否安全的话,使用ConcurrentHashMap时同样的功能尽量使用这类的方法:putIfAbsent(K key, V value)remove(Object key, Object value) replace(K key, V value) replace(K key, V oldValue, V newValue)
登录百度帐号推荐应用
为兴趣而生,贴吧更懂你。或关于HashMap的一些深入探索与理解 - 推酷
关于HashMap的一些深入探索与理解
在java中,大家几乎是离不开对集合的使用的,像Map系列,List系列,Set系列,但是很多人没有了解过或者研究过这些集合类到底可以用在什么地方,并且有什么注意的地方,因此本文分Map系列和List系列以及Set系列来讲述集合背后的故事。
一,HashMap
HashMap的初始化容量是16,载入因子是0.75
HashMap的key和value都可以为null
HashMap是线程不安全的,HashMap判断线程不安全的方式是通过两个模数即modCount和HashIterator内部类的exceptedModCount是否相等来判断是否是出于多线程读写状态,如果不相等,则抛出ConcurrentModificationException异常,其中modCount在1.7以前的版本都是用valitle修饰的,但是从1.7开始就把这个修饰符去掉了,因为modCount是在修改了HashMap的结构之后加1,即modCount++,而大家都知道,valitle在多线程环境下无法保证原子性,因此1.7去掉这个修饰符也减少了一些性能损耗,至于如何确保并发无误,就可以用Collections.synchronizedMap和ConcurrentHashMap了。
二,Collections.synchronizedMap和ConcurrentHashMap线程安全的区别
大家都知道这两个都是确保使用Map时的线程安全方案,但是这两者又有什么区别,他们真的能够保证线程安全吗?
Collections.synchronizedMap方法是集合类Collections中的一个静态类,这个SynchronizedMap实现了Map和serializable接口,它的每个方法都是使用synchronized来达到同步的,但是查看源代码之后你会发现,当你调用了entrySet()方法之后,再从这个entrySet()方法返回Iterator迭代器,然后使用迭代器在多线程来remove()时,也会出现并发问题,为什么呢,因为这个iterator是Set接口的迭代器,跟这个静态类是没有关系了,这样也将导致会出现并发问题而抛出同步异常
ConcurrentHashMap实现了AbstractMap抽象类和ConcurrentMap接口,这个类的同步是由JVM的底层代码来负责同步处理的,前面说过modCount,在这个同步类中也有modCount,但是它在这个类的HashIterator中没有用到,而是使用Unsafe的锁管理机制来迭代数据的,因此保证了数据的线程安全
通过以上对二者的描述,可以知道,如果在多线程换进下确保不使用迭代器的情况下,二者都是可以使用的,但是如果使用迭代器,则只能选择ConcurrentHashMap类来实现,推荐在多线程条件下,都使用ConcurrentHashMap,这样避免造成以后的尴尬......
已发表评论数()
&&登&&&陆&&
已收藏到推刊!
请填写推刊名
描述不能大于100个字符!
权限设置: 公开
仅自己可见Scala并发 - ImportNew
Runnable/Callable
Runnable只有一个没有返回值的方法
trait Runnable {
def run(): Unit
Callable的方法和run类似,只不过它有一个返回值
trait Callable[V] {
def call(): V
Scala的并发是建立在Java的并发模型上的。
在Sun的JVM上,对于一个IO密集型的任务,我们可以在单机上运行成千上万的线程。
Thread是通过Runnable构造的。要运行一个Runnable的run方法,你需要调用对应线程的start方法。
scala& val hello = new Thread(new Runnable {
def run() {
println(&hello world&)
hello: java.lang.Thread = Thread[Thread-3,5,main]
scala& hello.start
hello world
当你看见一个实现Runnable的类,你应该明白它会被放到一个线程里去执行的。
一段单线程的代码
下面是一段代码片段,它可以运行,但是会有问题。
import java.net.{Socket, ServerSocket}
import java.util.concurrent.{Executors, ExecutorService}
import java.util.Date
class NetworkService(port: Int, poolSize: Int) extends Runnable {
val serverSocket = new ServerSocket(port)
def run() {
while (true) {
// 这里会阻塞直到有连接进来
val socket = serverSocket.accept()
(new Handler(socket)).run()
class Handler(socket: Socket) extends Runnable {
def message = (Thread.currentThread.getName() + &\n&).getBytes
def run() {
socket.getOutputStream.write(message)
socket.getOutputStream.close()
(new NetworkService(2020, 2)).run
每个请求都会把当前线程的名称main作为响应。
这段代码最大的问题在于一次只能够响应一个请求!
你可以对每个请求都单独用一个线程来响应。只需要把
(new Handler(socket)).run()
(new Thread(new Handler(socket))).start()
但是如果你想要复用线程或者对于线程的行为要做一些其他的控制呢?
随着Java 5的发布,对于线程的管理需要一个更加抽象的接口。
你可以通过Executors对象的静态方法来取得一个ExecutorService对象。这些方法可以让你使用各种不同的策略来配置一个ExecutorService,例如线程池。
下面是我们之前的阻塞式网络服务器,现在改写成可以支持并发请求。
import java.net.{Socket, ServerSocket}
import java.util.concurrent.{Executors, ExecutorService}
import java.util.Date
class NetworkService(port: Int, poolSize: Int) extends Runnable {
val serverSocket = new ServerSocket(port)
val pool: ExecutorService = Executors.newFixedThreadPool(poolSize)
def run() {
while (true) {
// This will block until a connection comes in.
val socket = serverSocket.accept()
pool.execute(new Handler(socket))
} finally {
pool.shutdown()
class Handler(socket: Socket) extends Runnable {
def message = (Thread.currentThread.getName() + &\n&).getBytes
def run() {
socket.getOutputStream.write(message)
socket.getOutputStream.close()
(new NetworkService(2020, 2)).run
从下面的示例中,我们可以大致了解内部的线程是怎么进行复用的。
$ nc localhost 2020
pool-1-thread-1
$ nc localhost 2020
pool-1-thread-2
$ nc localhost 2020
pool-1-thread-1
$ nc localhost 2020
pool-1-thread-2
一个Future代表一次异步计算的操作。你可以把你的操作包装在一个Future里,当你需要结果的时候,你只需要简单调用一个阻塞的get()方法就好了。一个Executor返回一个Future。如果你使用Finagle RPC的话,你可以使用Future的实例来保存还没有到达的结果。
FutureTask是一个可运行的任务,并且被设计成由Executor进行运行。
val future = new FutureTask[String](new Callable[String]() {
def call(): String = {
searcher.search(target);
executor.execute(future)
现在我需要结果,那就只能阻塞到直到结果返回。
val blockingResult = future.get()
参考 有大量使用Future的示例,也有一些组合使用的例子。Effective Scala中也有关于的内容。
线程安全问题
class Person(var name: String) {
def set(changedName: String) {
name = changedName
这个程序在多线程的环境下是不安全的。如果两个线程都有同一个Person示例的引用,并且都调用set方法,你没法预料在两个调用都结束的时候name会是什么。
在Java的内存模型里,每个处理器都允许在它的L1或者L2 cache里缓存变量,所以两个在不同处理器上运行的线程对于相同的数据有种不同的视图。
下面我们来讨论一下可以强制线程的数据视图保持一致的工具。
互斥量(Mutex)提供了锁定资源的语法。当你进入一个互斥量的时候,你会获得它。在JVM里使用互斥量最常用的方式就是在一个对象上进行同步访问。在这里,我们会在Person上进行同步访问。
在JVM里,你可以对任何非null的对象进行同步访问。
class Person(var name: String) {
def set(changedName: String) {
this.synchronized {
name = changedName
随着Java 5对于内存模型的改变,volatile和synchronized的作用基本相同,除了一点,volatile也可以用在null上。
synchronized提供了更加细粒度的加锁控制。而volatile直接是对每次访问进行控制。
class Person(@volatile var name: String) {
def set(changedName: String) {
name = changedName
AtomaticReference
同样的,在Java 5中新增了一系列底层的并发原语。AtomicReference类就是其中一个。
import java.util.concurrent.atomic.AtomicReference
class Person(val name: AtomicReference[String]) {
def set(changedName: String) {
name.set(changedName)
它们都有额外的消耗吗?
AutomicReference是这两种方式中最耗性能的,因为如果你要取得对应的值,则需要经过方法分派(method dispatch)的过程。
volatile和synchronized都是通过Java内置的monitor来实现的。在没有竞争的情况下,monitor对性能的影响非常小。由于synchronized允许你对代码进行更加细粒度的加锁控制,这样就可以减小加锁区,进而减小竞争,因此synchronized应该是最佳的选择。
当你进入同步块,访问volatile引用,或者引用AtomicReference,Java会强制要求处理器刷新它们的缓存流水线,从而保证数据的一致性。
如果我这里说错了,请指正出来。这是一个很复杂的主题,对于这个主题肯定需要花费大量的时间来进行讨论。
其他来自Java 5的优秀工具
之前提到了AtomicReference,除了它之外,Java 5还提供了很多其他有用的工具。
CountDownLatch
CountDownLatch是供多个进程进行通信的一个简单机制。
val doneSignal = new CountDownLatch(2)
doAsyncWork(1)
doAsyncWork(2)
doneSignal.await()
println(&both workers finished!&)
除此之外,它对于单元测试也是很有用的。假设你在做一些异步的工作,并且你想要保证所有的功能都完成了。你只需要让你的函数都对latch进行countDown操作,然后在你的测试代码里进行await。
AtomicInteger/Long
由于对于Int和Long的自增操作比较常见,所以就增加了AtomicInteger和AtomicLong。
AtomicBoolean
我想我没有必要来解释这个的作用了。
读写锁(ReadWriteLock)
ReadWriteLock可以实现读写锁,读操作只会在写者加锁的时候进行阻塞。
我们来构建一个非线程安全的搜索引擎
这是一个简单的非线程安全的倒排索引。我们这个反向排索引把名字的一部分映射到指定的用户。
下面是原生的假设只有单线程访问的写法。
注意这里的使用mutable.HashMap的另一个构造函数this()。
import scala.collection.mutable
case class User(name: String, id: Int)
class InvertedIndex(val userMap: mutable.Map[String, User]) {
def this() = this(new mutable.HashMap[String, User])
def tokenizeName(name: String): Seq[String] = {
name.split(& &).map(_.toLowerCase)
def add(term: String, user: User) {
userMap += term -& user
def add(user: User) {
tokenizeName(user.name).foreach { term =&
add(term, user)
我把具体怎么根据索引获取用户的方法暂时省略掉了,我们后面会来进行补充。
我们来让它变得安全
在上面的倒排索引的示例里,userMap是没法保证线程安全的。多个客户端可以同时尝试去添加元素,这样会产生和之前Person示例里相似的问题。
因为userMap本身不是线程安全的,那么我们怎么能够保证每次只有一个线程对它进行修改呢?
你需要在添加元素的时候给userMap加锁。
def add(user: User) {
userMap.synchronized {
tokenizeName(user.name).foreach { term =&
add(term, user)
不幸的是,上面的做法有点太粗糙了。能在互斥量(mutex)外面做的工作尽量都放在外面做。记住我之前说过,如果没有竞争的话,加锁的代价是非常小的。如果你在临界区尽量少做操作,那么竞争就会非常少。
def add(user: User) {
// tokenizeName was measured to be the most expensive operation.
// tokenizeName 这个操作是最耗时的。
val tokens = tokenizeName(user.name)
tokens.foreach { term =&
userMap.synchronized {
add(term, user)
SynchronizedMap
我们可以通过使用SynchronizedMap trait来使得一个可变的(mutable)HashMap具有同步机制。
我们可以扩展之前的InvertedIndex,给用户提供一种构建同步索引的简单方法。
import scala.collection.mutable.SynchronizedMap
class SynchronizedInvertedIndex(userMap: mutable.Map[String, User]) extends InvertedIndex(userMap) {
def this() = this(new mutable.HashMap[String, User] with SynchronizedMap[String, User])
如果你去看具体的实现的话,你会发现SynchronizedMap只是在每个方法上都加上了同步访问,因此它的安全是以牺牲性能为代价的。
Java ConcurrentHashMap
Java里有一个很不错的线程安全的ConcurrentHashMap。幸运的是,JavaConverter可以使得我们通过Scala的语法来使用它。
实际上,我们可以无缝地把我们新的,线程安全的InvertedIndex作为老的非线程安全的一个扩展。
import java.util.concurrent.ConcurrentHashMap
import scala.collection.JavaConverters._
class ConcurrentInvertedIndex(userMap: collection.mutable.ConcurrentMap[String, User])
extends InvertedIndex(userMap) {
def this() = this(new ConcurrentHashMap[String, User] asScala)
现在来加载我们的InvertedIndex
最原始的方法
trait UserMaker {
def makeUser(line: String) = line.split(&,&) match {
case Array(name, userid) =& User(name, userid.trim().toInt)
class FileRecordProducer(path: String) extends UserMaker {
def run() {
Source.fromFile(path, &utf-8&).getLines.foreach { line =&
index.add(makeUser(line))
对于文件里的每一行字符串,我们通过调用makeUser来生成一个User,然后通过add添加到InvertedIndex里。如果我们并发访问一个InvertedIndex,我们可以并行调用add方法,因为makeUser方法没有副作用,它本身就是线程安全的。
我们不能并行读取一个文件,但是我们可以并行构造User,并且并行将它添加到索引里。
解决方案:生产者/消费者
实现非同步计算的,通常采用的方法就是将生产者同消费者分开,并让它们通过队列(queue)来进行通信。让我们用下面的例子来说明我们是怎么实现搜索引擎的索引的。
import java.util.concurrent.{BlockingQueue, LinkedBlockingQueue}
// Concrete producer
class Producer[T](path: String, queue: BlockingQueue[T]) extends Runnable {
def run() {
Source.fromFile(path, &utf-8&).getLines.foreach { line =&
queue.put(line)
// 抽象的消费者
abstract class Consumer[T](queue: BlockingQueue[T]) extends Runnable {
def run() {
while (true) {
val item = queue.take()
consume(item)
def consume(x: T)
val queue = new LinkedBlockingQueue[String]()
//一个生产者线程
val producer = new Producer[String](&users.txt&, q)
new Thread(producer).start()
trait UserMaker {
def makeUser(line: String) = line.split(&,&) match {
case Array(name, userid) =& User(name, userid.trim().toInt)
class IndexerConsumer(index: InvertedIndex, queue: BlockingQueue[String]) extends Consumer[String](queue) with UserMaker {
def consume(t: String) = index.add(makeUser(t))
// 假设我们的机器有8个核
val cores = 8
val pool = Executors.newFixedThreadPool(cores)
// 每个核设置一个消费者
for (i &- i to cores) {
pool.submit(new IndexerConsumer[String](index, q))
原文链接:
- 译文链接: [ 转载请保留原文出处、译者和译文链接。]
关于作者:
Java开发工程师,业余翻译
微信关注: ImportNew
分享Java相关的技术文章、工具资源和热点资讯。扫描加关注,碎片时间提高Java开发技能!
使用过用反射来解析注解,但是还没有使用过编译时注解,只知道切面编程有种方式就是类似这样的道理的,不是...
关于ImportNew
ImportNew 专注于 Java 技术分享。于日 11:11正式上线。是的,这是一个很特别的时刻 :)
ImportNew 由两个 Java 关键字 import 和 new 组成,意指:Java 开发者学习新知识的网站。 import 可认为是学习和吸收, new 则可认为是新知识、新技术圈子和新朋友……
– 写了文章?看干货?去头条!
– 为IT单身男女服务的征婚传播平台
– 优秀的工具资源导航
– 活跃 & 专业的翻译小组
– 国内外的精选博客文章
– JavaScript, HTML5, CSS
– 专注Android技术分享
– 专注iOS技术分享
– 专注Java技术分享
– 专注Python技术分享
新浪微博:
微信号:importnew
反馈建议:@
广告与商务合作QQ:
& 2015 ImportNew水木-Java技术-我想拿ConcurrentHashMap做cache,但是怎么做lru呢?
主题文章列表
下一页&共1页&
我想拿ConcurrentHashMap做cache,但是怎么做lru呢?
发信人:&snnn&(cm),&信区:&Java
标&&题:&我想拿ConcurrentHashMap做cache,但是怎么做lru呢?
发信站:&水木社区&(Thu&Jul&14&16:13:23&2011),&站内
我想拿ConcurrentHashMap做cache,但是不知道怎么做lru。
一个最普通的想法就是:做一个链表,每次get操作执行完之后,去这个链表先对这个key做remove操作,然后把它加到末尾。当cache的capacity达到上限的时候,从这个lru链表的头部删除一个元素。但是这个remove、append操作必须是原子的,否则这个lru链表中可能就会有重复的元素。另外就是,lru和cache这两个对象之间怎么保证一致性?采用ConcurrentHashMap就是为了减少锁,如果非要为了lru再加一个锁,就很失败了。
有代码或设计可供参考吗?
※&来源:·水木社区&newsmth.net·[FROM:&180.88.126.*]
Re: 我想拿ConcurrentHashMap做cache,但是怎么做lru呢?
发信人:&spacehero&(-)__(-),&信区:&Java
标&&题:&Re:&我想拿ConcurrentHashMap做cache,但是怎么做lru呢?
发信站:&水木社区&(Thu&Jul&14&16:14:56&2011),&站内
mons.collections.map.LRUMap?
【&在&snnn&(cm)&的大作中提到:&】
:&我想拿ConcurrentHashMap做cache,但是不知道怎么做lru。
:&一个最普通的想法就是:做一个链表,每次get操作执行完之后,去这个链表先对这个key做remove操作,然后把它加到末尾。当cache的capacity达到上限的时候,从这个lru链表的头部删除一个元素。但是这个remove、append操作必须是原子的,否则这个lru链表中可能就会有重复的元素。另外就是,lru和cache这两个对象之间怎么保证一致性?采用ConcurrentHashMap就是为了减少锁,如果非要为了lru再加一个锁,就很失败了。
:&有代码或设计可供参考吗?
※&来源:·水木社区&http://newsmth.net·[FROM:&124.193.167.*]
Re: 我想拿ConcurrentHashMap做cache,但是怎么做lru呢?
发信人:&snnn&(cm),&信区:&Java
标&&题:&Re:&我想拿ConcurrentHashMap做cache,但是怎么做lru呢?
发信站:&水木社区&(Thu&Jul&14&16:23:09&2011),&站内
这个不是线程安全的。如果不考虑并发粒度的问题,这个确实很容易实现。
【&在&spacehero&(-)__(-)&的大作中提到:&】
:&mons.collections.map.LRUMap?
※&来源:·水木社区&newsmth.net·[FROM:&180.88.126.*]
Re: 我想拿ConcurrentHashMap做cache,但是怎么做lru呢?
发信人:&spacehero&(-)__(-),&信区:&Java
标&&题:&Re:&我想拿ConcurrentHashMap做cache,但是怎么做lru呢?
发信站:&水木社区&(Thu&Jul&14&16:23:09&2011),&站内
对了,hbase里的block&cache就是个基于ConcurrentHashMap的LRU实现
【&在&spacehero&(-)__(-)&的大作中提到:&】
:&mons.collections.map.LRUMap?
※&来源:·水木社区&http://newsmth.net·[FROM:&124.193.167.*]
Re: 我想拿ConcurrentHashMap做cache,但是怎么做lru呢?
发信人:&hunterlee&(hunter&),&信区:&Java
标&&题:&Re:&我想拿ConcurrentHashMap做cache,但是怎么做lru呢?
发信站:&水木社区&(Thu&Jul&14&16:58:44&2011),&站内
你可以查查memcached的实现,它也是多线程+锁
我觉着你不需要太担心锁的问题,因为你所有的操作都在内存中执行,而且每种操作都很简单,耗时很小,另外,线程之间的锁也是非常轻量级的,它对你的性能影响真的很小很小;
【&在&snnn&(cm)&的大作中提到:&】
:&我想拿ConcurrentHashMap做cache,但是不知道怎么做lru。
:&一个最普通的想法就是:做一个链表,每次get操作执行完之后,去这个链表先对这个key做remove操作,然后把它加到末尾。当cache的capacity达到上限的时候,从这个lru链表的头部删除一个元素。但是这个remove、append操作必须是原子的,否则这个lru链表中可能就会有重复的元素。另外就是,lru和cache这两个对象之间怎么保证一致性?采用ConcurrentHashMap就是为了减少锁,如果非要为了lru再加一个锁,就很失败了。
:&有代码或设计可供参考吗?
※&来源:·水木社区&http://newsmth.net·[FROM:&123.125.220.*]
Re: 我想拿ConcurrentHashMap做cache,但是怎么做lru呢?
发信人:&djangofan&(tuscany&jj&o),&信区:&Java
标&&题:&Re:&我想拿ConcurrentHashMap做cache,但是怎么做lru呢?
发信站:&水木社区&(Thu&Jul&14&17:29:51&2011),&站内
用LinkedHashMap
【&在&snnn&(cm)&的大作中提到:&】
:&我想拿ConcurrentHashMap做cache,但是不知道怎么做lru。
:&一个最普通的想法就是:做一个链表,每次get操作执行完之后,去这个链表先对这个key做remove操作,然后把它加到末尾。当cache的capacity达到上限的时候,从这个lru链表的头部删除一个元素。但是这个remove、append操作必须是原子的,否则这个lru链表中可能就会有重复的元素。另外就是,lru和cache这两个对象之间怎么保证一致性?采用ConcurrentHashMap就是为了减少锁,如果非要为了lru再加一个锁,就很失败了。
:&有代码或设计可供参考吗?
※&来源:·水木社区&http://newsmth.net·[FROM:&124.42.13.*]
Re: 我想拿ConcurrentHashMap做cache,但是怎么做lru呢?
发信人:&leechong&(忽悠&&@$$@),&信区:&Java
标&&题:&Re:&我想拿ConcurrentHashMap做cache,但是怎么做lru呢?
发信站:&水木社区&(Thu&Jul&14&22:25:46&2011),&站内
1,&LRUHashmap&同步化
2,&ConcurrentLRUHashMap,taobao那边开发的。
/p/jue/source/browse/trunk/jue/src/com/googlecode/ju
e/util/ConcurrentLRUHashMap.java?spec=svn21&r=21
【&在&snnn&(cm)&的大作中提到:&】
:&我想拿ConcurrentHashMap做cache,但是不知道怎么做lru。
:&一个最普通的想法就是:做一个链表,每次get操作执行完之后,去这个链表先对这个
key做remove操作,然后把它加到末尾。当cache的capacity达到上限的时候,从这个
lru链表的头部删除一个元素。但是这个remove、append操作必须是原子的,否则这
个lru链表中可能就会有重复的元素。另外就是,lru和cache这两个对象之间怎么保证一
致性?采用ConcurrentHashMap就是为了减少锁,如果非要为了lru再加一个锁,就很
:&有代码或设计可供参考吗?
&&&&&&&&&&&&&&&&水木社区凉粉证&-0000&号&&&&&&&&&&
&&&&&&&&&┏^ǒ^*★*^ǒ^*☆*^ǒ^*★*^ǒ^*☆*^ǒ^&┓
&&&&&&&&&┃╭の╮┏┯┓┏┯┓&┏┯┓┏┯┓&&╬&&┃
&&&&&&&&&┃&╲╱&┠支┨┠持Lovの靓┨┠颖┨╭║╮┃
&&&&&&&&&┃┗歌┛┗┷┛┗┷┛&┗┷┛┗┷┛&╲╱&┃
&&&&&&&&&┗^ǒ^*★*^ǒ^*☆*^ǒ^*★*^ǒ^*☆*^ǒ^&┛
※&来源:·水木社区&http://newsmth.net·[FROM:&125.119.16.*]
下一页&共1页&多线程之Map:Hashtable&HashMap&以及ConcurrentHashMap
1、Map体系
Hashtable是JDK
5之前Map唯一线程安全的内置实现(Collections.synchronizedMap不算)。Hashtable继承的是Dictionary(Hashtable是其唯一公开的子类),并不继承AbstractMap或者HashMap.尽管Hashtable和HashMap的结构非常类似,但是他们之间并没有多大联系。
ConcurrentHashMap是HashMap的线程安全版本,ConcurrentSkipListMap是TreeMap的线程安全版本。
最终可用的线程安全版本Map实现是ConcurrentHashMap/ConcurrentSkipListMap/Hashtable/Properties四个,但是Hashtable是过时的类库,因此如果可以的应该尽可能的使用ConcurrentHashMap和ConcurrentSkipListMap.
2、Hashtable、HashMap异同
(1)Hashtable是Dictionary的子类
public class Hashtable
extends Dictionary
implements Map, Cloneable, java.io.Serializable
public class HashMap
extends AbstractMap
implements Map, Cloneable, Serializable
HashMap和Hashtable都是Map接口的一个实现类;
(2)Hashtable中的方法是同步的(),而HashMap中的方法在默认情况下不是同步的。即是说,在多线程应用程序中,不用专门的操作就安全地可以使用Hashtable了;而对于HashMap,则需要额外的同步机制。但HashMap的同步问题可通过Collections的一个静态方法得到解决:
public static Map synchronizedMap(Map m)
这个方法返回一个同步的Map,也就是说返回的Map是线程安全的。需要注意的是,对返回的map进行迭代时,必须手动在返回的map上进行同步,否则将会导致不确定的行为:
Map m = Collections.synchronizedMap(new HashMap());
Set s = m.keySet(); // Needn't be in synchronized block
synchronized(m) { // Synchronizing on m, not s!
Iterator i = s.iterator(); // Must be in synchronized block
while (i.hasNext())
foo(i.next());
(3)在HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。
当get()方法返回null值时,即可以表示HashMap中没有该键,也可以表示该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键,而应该用containsKey()方法来判断。Hashtable的键值不能为null,否则:java.lang.NullPointerException
(4)HashTable使用Enumeration,HashMap使用Iterator。
以上只是表面的不同,它们的实现也有很大的不同。
(5)HashTable中hash数组默认大小是11,增加的方式是
old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数。
(6)哈希值的使用不同,HashTable直接使用对象的hashCode,代码是这样的:
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.
而HashMap重新计算hash值,而且用与代替求模,比如HashMap的put方法
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry e = table[i]; e != e = e.next) {
if (e.hash == hash && ((k = e.key) == key ||
key.equals(k))) {
V oldValue = e.
e.recordAccess(this);
return oldV
modCount++;
addEntry(hash, key, value, i);
static int hash(int h) {
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load
h ^= (h &&& 20) ^ (h &&& 12);
return h ^ (h &&& 7) ^ (h &&& 4);
static int indexFor(int h, int length) {
return h & (length-1);
总之,HashTable是多线程安全的,不需要人工同步,但性能方面会差一点;而HashMap不能直接用于多线程。而
ConcurrentHashMap的出现正解决上诉问题。它是HashMap的线程安全版本,性能方面也优于HashTable。ConcurrentSkipListMap是TreeMap的线程安全版本。
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。}

我要回帖

更多关于 php线程安全 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信