java – 用于同步获取/释放的无锁保护
我有一个共享的临时文件资源,分为4K块(或一些这样的值).文件中的每个4K由从零开始的索引表示.对于此共享资源,我跟踪正在使用的4K块索引,并始终返回未使用的最低索引4K块,如果所有正在使用,则返回-1.
索引的这个ResourceSet类有一个公共的获取和释放方法,它们都使用同步锁,其持续时间大约相当于生成4个随机数(昂贵,cpu-wise). 因此,从下面的代码中可以看出,我使用AtomicInteger“计数信号量”来防止大量线程在acquire()上同时进入临界区,返回-1(现在不可用)线程太多了. 目前,我使用100的常量作为紧密的CAS循环来尝试增加获取中的原子整数,并将最大线程数的常量设置为10然后允许进入临界区,这足以产生争用.我的问题是,对于中等到高负载的servlet引擎,这些常量应该是什么?有几个线程试图访问这些4K块? public class ResourceSet { // ??? what should this be // maximum number of attempts to try to increment with CAS on acquire private static final int CAS_MAX_ATTEMPTS = 50; // ??? what should this be // maximum number of threads contending for lock before returning -1 on acquire private static final int CONTENTION_MAX = 10; private AtomicInteger latch = new AtomicInteger(0); ... member variables to track free resources private boolean aquireLatchForAquire () { for (int i = 0; i < CAS_MAX_ATTEMPTS; i++) { int val = latch.get(); if (val == -1) throw new AssertionError("bug in ResourceSet"); // this means more threads than can exist on any system,so its a bug! if (!latch.compareAndSet(val,val+1)) continue; if (val < 0 || val >= CONTENTION_MAX) { latch.decrementAndGet(); // added to fix BUG that comment pointed out,thanks! return false; } } return false; } private void aquireLatchForRelease () { do { int val = latch.get(); if (val == -1) throw new AssertionError("bug in ResourceSet"); // this means more threads than can exist on any system,so its a bug! if (latch.compareAndSet(val,val+1)) return; } while (true); } public ResourceSet (int totalResources) { ... initialize } public int acquire (ResourceTracker owned) { if (!aquireLatchForAquire()) return -1; try { synchronized (this) { ... algorithm to compute minimum free resoource or return -1 if all in use return resourceindex; } } finally { latch.decrementAndGet(); } } public boolean release (ResourceIter iter) { aquireLatchForRelease(); try { synchronized (this) { ... iterate and release all resources } } finally { latch.decrementAndGet(); } } } 解决方法
编写一个优秀且高性能的自旋锁实际上非常复杂,需要很好地理解内存障碍.仅仅选择一个常数并不会削减它,绝对不会是便携式的.谷歌的gperftools有
an example你可以看,但可能比你需要的更复杂.
如果您真的想减少对锁的争用,您可能需要考虑使用更细粒度和乐观的方案.一个简单的方法是将你的块分成n组并将锁与每个组相关联(也称为剥离).这将有助于减少争用并提高吞吐量,但无助于减少延迟.您还可以将AtomicBoolean与每个块和CAS相关联以获取它(在发生故障时重试).在处理无锁算法时要小心,因为它们往往很难做到正确.如果你做对了,它可以大大减少获取块的延迟. 请注意,在不知道块选择算法的样子的情况下,很难提出更细粒度的方法.我还假设你确实遇到了性能问题(它已被分析并且一切都有). 虽然我对它,你的螺旋锁实现是有缺陷的.你永远不应该直接在CAS上旋转,因为你是在发送内存障碍.任何严重的争用(与thundering-herd problem相关)都会非常缓慢.最低限度是首先在CAS之前检查变量的可用性(如果没有屏障读取,则会很简单).更好的方法是不要让所有的线程都在相同的值上旋转.这应该避免关联的缓存行在您的核心之间进行乒乓. 请注意,我不知道Java中的原子操作与哪种类型的内存屏障相关联,因此我的上述建议可能不是最佳或正确的. 最后,The Art Of Multiprocessor Programming是一本有趣的书,可以让我更好地了解我在这个答案中喷出的所有非感觉. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |