public class CheckThenAct { /** * Unbound shared message queue; */ private final ConcurrentLinkedQueue<String> q; /** * Flag indicating whether operation should proceed; */ private final AtomicBoolean proceed = new AtomicBoolean(true); public CheckThenAct(ConcurrentLinkedQueue<String> q) { this.q = q; } public boolean putMessage(String msg) { return proceed.get() ? q.offer(msg) : false; } public boolean stop() { return proceed.compareAndSet(true, false); } }In the putMessage() method we have "check-than-act" concurrency problem. I.e. when in thread1, thread2, ..., thread20 we pass proceed.get() but just right after that in thread23 we call stop(), i.e. no more messages, seriously; but in fact twenty messages being added to the q. Since putMessage() must be highly concurrent I can't make it(and stop()) synchronized. Solution is - to use RRWL:
... private final ReentrantReadWriteLock rw = new ReentrantReadWriteLock(); ... public boolean putMessage(String msg) { if (!rw.readLock().tryLock()) return false; try { return proceed.get() ? q.offer(msg) : false; } finally { rw.readLock().unlock(); } } public boolean stop() { rw.writeLock().lock(); try { return proceed.compareAndSet(true, false); } finally { rw.writeLock().unlock(); } }Let me explain why:
- Read lock in putMessage() doesn't bring serialized behaviour, method still is highly concurrent;
- Read lock being acquired only when there is no write lock;
- Write lock being acquired only when there are no read locks;
- Write lock is exclusive;
So, main demand satisfied - truly shared access in putMessage() and remedy the "check-then-act" behaviour!
Thanks for reading!
No comments:
Post a Comment