锁定老帖子 主题:LRU缓存的实现
精华帖 (0) :: 良好帖 (3) :: 新手帖 (13) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2009-12-09
最后修改:2009-12-09
火星来客 写道 mikeandmore 写道 原来也是搞链表鸟。。。嗯。。。还是自己写靠谱。。。-,-
于是 我觉得lz的实现有点问题。。。 readlock和readlock互溶 于是会同时发生get,这估计也是lz想要的。 但是看看hashedlinklist的代码 第323行,会开始对lru queue进行访问,要把当前的访问放在queue head。这会发生争抢。造成链表出现一些诡异的现象。 这个也是我用synchronized的原因,get操作会修改链表,用ReentrantLock没问题,但是要用同一把锁做get和put,读写锁用在这里不合适 嗯。对。 而且我觉得这个东西如果用spinlock会好一些。。这么短操作不要陷入内核... |
|
返回顶楼 | |
发表时间:2009-12-09
mikeandmore 写道 火星来客 写道 mikeandmore 写道 原来也是搞链表鸟。。。嗯。。。还是自己写靠谱。。。-,-
于是 我觉得lz的实现有点问题。。。 readlock和readlock互溶 于是会同时发生get,这估计也是lz想要的。 但是看看hashedlinklist的代码 第323行,会开始对lru queue进行访问,要把当前的访问放在queue head。这会发生争抢。造成链表出现一些诡异的现象。 这个也是我用synchronized的原因,get操作会修改链表,用ReentrantLock没问题,但是要用同一把锁做get和put,读写锁用在这里不合适 嗯。对。 而且我觉得这个东西如果用spinlock会好一些。。这么短操作不要陷入内核... 250w/s的操作我想对于一个cache来说应该没有问题了,只是在我的测试中cpu上升到95%,应该是内核互斥命令造成的。不知道用spinlock会不会能降低cpu的消耗 |
|
返回顶楼 | |
发表时间:2009-12-09
火星来客 写道 mikeandmore 写道 火星来客 写道 mikeandmore 写道 原来也是搞链表鸟。。。嗯。。。还是自己写靠谱。。。-,-
于是 我觉得lz的实现有点问题。。。 readlock和readlock互溶 于是会同时发生get,这估计也是lz想要的。 但是看看hashedlinklist的代码 第323行,会开始对lru queue进行访问,要把当前的访问放在queue head。这会发生争抢。造成链表出现一些诡异的现象。 这个也是我用synchronized的原因,get操作会修改链表,用ReentrantLock没问题,但是要用同一把锁做get和put,读写锁用在这里不合适 嗯。对。 而且我觉得这个东西如果用spinlock会好一些。。这么短操作不要陷入内核... 250w/s的操作我想对于一个cache来说应该没有问题了,只是在我的测试中cpu上升到95%,应该是内核互斥命令造成的。不知道用spinlock会不会能降低cpu的消耗 理论上只会升高。不过cache这个东西本身就是越快越好的。 |
|
返回顶楼 | |
发表时间:2009-12-09
最后修改:2009-12-09
mikeandmore 写道 理论上只会升高。不过cache这个东西本身就是越快越好的。 我认为是够用就好, 比如说我做一个cache app,每秒可以接受30w次socket请求,那我这个cache再快也没有用,在这种场景下,我们可以考虑如何降低cpu的消耗,而不是一味的追求快。 总之看场景吧,在速度和cpu的负载之间取一个平衡点 |
|
返回顶楼 | |
发表时间:2009-12-09
火星来客 写道 mikeandmore 写道 理论上只会升高。不过cache这个东西本身就是越快越好的。 我认为是够用就好, 比如说我做一个cache app,每秒可以接受30w次socket请求,那我这个cache再快也没有用,在这种场景下,我们可以考虑如何降低cpu的消耗,而不是一味的追求快。 总之看场景吧,在速度和cpu的负载之间取一个平衡点 嗯,我不是做这个场景的。嘿嘿。。。 要是数据库的话,这块东西必然是越快越好。。。因为io操作远远比request的数量多。 anyway, tuning is tricky. |
|
返回顶楼 | |
发表时间:2009-12-09
你们俩的回复速度惊人。
单纯从缓存角度,synchronized是最好的,但是考虑并发读写上的不同逻辑,当一个线程put时,另一个线程是无法get的。 同一个线程重入get的间隔基本可以保证链表重排完成,而且两次进入该锁的可能性并不高。 技术上存在你们说的问题,但实际上并不太会发生。因此,我不打算做改变。 |
|
返回顶楼 | |
发表时间:2009-12-09
marshan 写道 你们俩的回复速度惊人。
单纯从缓存角度,synchronized是最好的,但是考虑并发读写上的不同逻辑,当一个线程put时,另一个线程是无法get的。 同一个线程重入get的间隔基本可以保证链表重排完成,而且两次进入该锁的可能性并不高。 技术上存在你们说的问题,但实际上并不太会发生。因此,我不打算做改变。 不会发生吗?事实上发生的几率相当大,特别是在高并发并且缓存数据急剧增加的情况下。我们的系统吃过苦头,因为这个问题OOM。这本身是不安全的代码,拿出来误导人是很不应该的。 |
|
返回顶楼 | |
发表时间:2009-12-09
marshan 写道
OK,感谢二位!如果可能,请给出重入读锁的失败例子。
package lru; import java.util.concurrent.CyclicBarrier; public class LRUMapConcurrentTest { static int limit = 1000; static int count = 100000; static int threadCount = 100; private final OjadbMap<Integer, Integer> map = new OjadbMap<Integer, Integer>( 1000); class TestThread implements Runnable { private final CyclicBarrier barrier; private final int beginIndex; public TestThread(int beginIndex, CyclicBarrier barrier) { super(); this.beginIndex = beginIndex; this.barrier = barrier; } public void run() { try { barrier.await(); for (int i = beginIndex; i < beginIndex + count; i++) map.put(i, i); for (int i = beginIndex; i < beginIndex + count; i++) map.get(i); barrier.await(); } catch (Exception e) { throw new RuntimeException(e); } } } public void concurrentTest() { CyclicBarrier barrier = new CyclicBarrier(threadCount + 1); for (int i = 0; i < threadCount; i++) { new Thread(new TestThread(i, barrier)).start(); } try { barrier.await(); barrier.await(); } catch (Exception e) { throw new RuntimeException(e); } System.out.println("Current map size:" + map.size()); } public static void main(String[] args) { new LRUMapConcurrentTest().concurrentTest(); } } 尽管讲LRU的maxCapacity设置为1000,然而最后的结果却是maxCapacity的十倍,最大限制形同虚设,如果你将循环次数count设置为Integer.MAX_VALUE,那么很容易跑出个内存溢出。既然这样的代码是放在google code上,我想基于对用户负责的态度,也不应该让这样明显的并发隐患存在于你的系统中吧。可能口气重了点,我想表达的意思是对于并发,不要用“可能、我认为不会”这样的字眼,事实上看起来很小的错误在高并发下都将被放大并显现。
|
|
返回顶楼 | |
发表时间:2009-12-10
marshan 写道 你们俩的回复速度惊人。
单纯从缓存角度,synchronized是最好的,但是考虑并发读写上的不同逻辑,当一个线程put时,另一个线程是无法get的。 同一个线程重入get的间隔基本可以保证链表重排完成,而且两次进入该锁的可能性并不高。 技术上存在你们说的问题,但实际上并不太会发生。因此,我不打算做改变。 1 这个东西很难测到的 2 这个东西还是可以测到的。。。见上面的case |
|
返回顶楼 | |
发表时间:2009-12-10
dennis_zane 写道
尽管讲LRU的maxCapacity设置为1000,然而最后的结果却是maxCapacity的十倍,最大限制形同虚设,如果你将循环次数count设置为Integer.MAX_VALUE,那么很容易跑出个内存溢出。既然这样的代码是放在google code上,我想基于对用户负责的态度,也不应该让这样明显的并发隐患存在于你的系统中吧。可能口气重了点,我想表达的意思是对于并发,不要用“可能、我认为不会”这样的字眼,事实上看起来很小的错误在高并发下都将被放大并显现。 测试结果: Current map size:1000
你的表达很中肯,我有的只有敬重和感谢!而且,我的项目或将考虑你们的建议,进行修改。 |
|
返回顶楼 | |