I have a tempfile share that divides into 4K pieces (or some such value). Each 4K file is represented by an index starting at zero. For this share, I track the indexes of the 4K blocks and always return the smallest indexed 4K block that is not used, or -1 if they are all used.
This ResourceSet class for indexes has a publicly available retrieval and release method, both of which use a synchronized lock, the duration of which is about the same as when generating 4 random numbers (expensive, cpu-wise).
Therefore, as you can see from the following code, I use AtomicInteger "counting semaphore" to prevent a large number of threads from simultaneously falling into the critical section when receiving (), return -1 (not available right now) if there are too many threads.
I am currently using the constant 100 for the CAS hard cycle to try to increase the atomic integer in the acquirer and the constant 10 for the maximum number of threads, then to allow a critical section that is long enough to create competition. My question is, what should these constants be for a moderately high-loaded servlet engine that has multiple threads trying to access these pieces of 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(); } } }
source share