锁定老帖子 主题:高速缓存实现
精华帖 (0) :: 良好帖 (0) :: 新手帖 (15) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2010-06-09
beneo 写道 soongbo 写道 kazy 写道 从另外的地方抄个例子过来,
ConcurrentHashMap<String,String> map; String getString(String name) { String x = map.get(name); if (x == null) { x = new String(); map.put(name, x); } return x; } 如果你只调用get(),或只调用put()时,ConcurrentHashMap是线程安全的。 但是,在你调用完get后,调用put之前, 如果有另外一个线程调用了map.put(name, x), 你再去执行map.put(name,x), 就很可能把前面的操作结果覆盖掉了。 所以,即使在线程安全的情况下, 你还是有可能违反原子操作的规则。 你提出的这个问题我个人觉得应该不会出现,因为在put前利用了putIfAbent来保证该key对应的FutureTask是第一次put到Map,如果不是第一次此时只需要等待已经put的FutureTask执行获得value的结果,然后分享胜利果实即可. 的确putIfAbent能够保证只有唯一一个key,不过可惜的是你在调用putIfAbent前,有可能多个线程一起计算同一个key的value 你觉得这个的开销大还是锁的开销大 看来你还没有看明白putIfAbsent()方法之前的代码,那里没有计算value,而是在putIfAbent判断了对应Key的FutureTask不存在后,在又FutureTask去执行创建Value的工作,所以这里没有性能开销。 |
|
返回顶楼 | |
发表时间:2010-06-09
soongbo 写道 beneo 写道 soongbo 写道 kazy 写道 从另外的地方抄个例子过来,
ConcurrentHashMap<String,String> map; String getString(String name) { String x = map.get(name); if (x == null) { x = new String(); map.put(name, x); } return x; } 如果你只调用get(),或只调用put()时,ConcurrentHashMap是线程安全的。 但是,在你调用完get后,调用put之前, 如果有另外一个线程调用了map.put(name, x), 你再去执行map.put(name,x), 就很可能把前面的操作结果覆盖掉了。 所以,即使在线程安全的情况下, 你还是有可能违反原子操作的规则。 你提出的这个问题我个人觉得应该不会出现,因为在put前利用了putIfAbent来保证该key对应的FutureTask是第一次put到Map,如果不是第一次此时只需要等待已经put的FutureTask执行获得value的结果,然后分享胜利果实即可. 的确putIfAbent能够保证只有唯一一个key,不过可惜的是你在调用putIfAbent前,有可能多个线程一起计算同一个key的value 你觉得这个的开销大还是锁的开销大 看来你还没有看明白putIfAbsent()方法之前的代码,那里没有计算value,而是在putIfAbent判断了对应Key的FutureTask不存在后,在又FutureTask去执行创建Value的工作,所以这里没有性能开销。 嗯,是凭映像说的。 其实简单的说,好几个线程new Callable<T> 这种代价可以接受的话,你这样写是没有问题的 引用 因为在利用FutureTask生成Value的过程中,有可能出现异常,所以这里需要while(true)来做轮询,知道对应key有一个对应的FutureTask放入Map中。
如果有异常你循环,那么下次循环还不是异常么?还不如检测出异常放个异常值来的好 还有我调用addElement(final K key, final V value),返回的都是最初计算出来的结果,这个是你想要的么 ,换句话说,你的缓存怎么去更新? |
|
返回顶楼 | |
发表时间:2010-06-09
beneo 写道 soongbo 写道 beneo 写道 soongbo 写道 kazy 写道 从另外的地方抄个例子过来,
ConcurrentHashMap<String,String> map; String getString(String name) { String x = map.get(name); if (x == null) { x = new String(); map.put(name, x); } return x; } 如果你只调用get(),或只调用put()时,ConcurrentHashMap是线程安全的。 但是,在你调用完get后,调用put之前, 如果有另外一个线程调用了map.put(name, x), 你再去执行map.put(name,x), 就很可能把前面的操作结果覆盖掉了。 所以,即使在线程安全的情况下, 你还是有可能违反原子操作的规则。 你提出的这个问题我个人觉得应该不会出现,因为在put前利用了putIfAbent来保证该key对应的FutureTask是第一次put到Map,如果不是第一次此时只需要等待已经put的FutureTask执行获得value的结果,然后分享胜利果实即可. 的确putIfAbent能够保证只有唯一一个key,不过可惜的是你在调用putIfAbent前,有可能多个线程一起计算同一个key的value 你觉得这个的开销大还是锁的开销大 看来你还没有看明白putIfAbsent()方法之前的代码,那里没有计算value,而是在putIfAbent判断了对应Key的FutureTask不存在后,在又FutureTask去执行创建Value的工作,所以这里没有性能开销。 嗯,是凭映像说的。 其实简单的说,好几个线程new Callable<T> 这种代价可以接受的话,你这样写是没有问题的 引用 因为在利用FutureTask生成Value的过程中,有可能出现异常,所以这里需要while(true)来做轮询,知道对应key有一个对应的FutureTask放入Map中。
如果有异常你循环,那么下次循环还不是异常么?还不如检测出异常放个异常值来的好 还有我调用addElement(final K key, final V value),返回的都是最初计算出来的结果,这个是你想要的么 ,换句话说,你的缓存怎么去更新? 这里的实现是没有考虑缓存更新的问题,因为缓存的更新其实是和业务逻辑挂钩的,具体需要根据业务逻辑是定制缓存策略。 |
|
返回顶楼 | |
发表时间:2010-06-09
最后修改:2010-06-09
这个是不是对象产生器~~~不是所谓的缓存吧
你那个判断什么的是不是写反了啊,怎么先循环再判断是否为空啊,而且add操作是可以并发的,但是你的key-value是一对一的啊,这样的等待就一点都不高速了啊~~ 可以考虑修改一下,变成一种对象池,缺少了就补充这样还实用点,事实上我们有某个项目就打算采取这个滴 |
|
返回顶楼 | |
发表时间:2010-06-09
sunson468 写道 这个是不是对象产生器~~~不是所谓的缓存吧
你那个判断什么的是不是写反了啊,怎么先循环再判断是否为空啊,而且add操作是可以并发的,但是你的key-value是一对一的啊,这样的等待就一点都不高速了啊~~ 可以考虑修改一下,变成一种对象池,缺少了就补充这样还实用点,事实上我们有某个项目就打算采取这个滴 针对你提出的问题,解释如下: 1.while循环是考虑在Futuretask在创建value过程中可能出现异常导致无法获得对应的value,这里是来做轮询的,只有成功创建了value并返回,从而结束轮询。 2.这样实现addElement的有一个很明显的优势就是:当第二个线程判断key在Map存在,且对应的Futuretask已经存在,那么第二个线程只需要等待创建该key对应的Futuretask的线程返回value,这样如果创建一个对象的性能开销很大的话,这个高速缓存就更能体现出"高速"吧。 |
|
返回顶楼 | |
发表时间:2010-06-10
soongbo 写道 sunson468 写道 这个是不是对象产生器~~~不是所谓的缓存吧
你那个判断什么的是不是写反了啊,怎么先循环再判断是否为空啊,而且add操作是可以并发的,但是你的key-value是一对一的啊,这样的等待就一点都不高速了啊~~ 可以考虑修改一下,变成一种对象池,缺少了就补充这样还实用点,事实上我们有某个项目就打算采取这个滴 针对你提出的问题,解释如下: 1.while循环是考虑在Futuretask在创建value过程中可能出现异常导致无法获得对应的value,这里是来做轮询的,只有成功创建了value并返回,从而结束轮询。 2.这样实现addElement的有一个很明显的优势就是:当第二个线程判断key在Map存在,且对应的Futuretask已经存在,那么第二个线程只需要等待创建该key对应的Futuretask的线程返回value,这样如果创建一个对象的性能开销很大的话,这个高速缓存就更能体现出"高速"吧。 1,没错,但是如果一直不被创建的呢?如何保证add线程的个数,采取线程池吗?那么整个线程池如何管理,还是说建立不同的线程池,你这个应该是通用组件,那么通用组件是否应该考虑一下你对整个应用无论是性能还是消耗上都得达到一定标准呢。似乎用阻塞队列都比这个无限循环要好很多吧,当然,可能我误解你的本意了。 2,也就是说你每次只会保存一个缓存,也就是说当我需要获取多个返回的时候也只能一个个等待。是这个意思吗?所以我提出池的概念,当然可能我没理解你这个key,它是否就是类似于spring的beanname一样的一种存在,事实上很多时候我们开销大的原因在于我们需要创建很多很多的这样的对象,而且都是不同的引用,而这些对象紧接着又都会被销毁,这个只要过度作用的对象的创建消耗才不被我们允许。 |
|
返回顶楼 | |