Mutually exclusive methods

I am on the road to learning multi-threaded Java programming. I have the following logic:

Suppose I have class A

class A { ConcurrentMap<K, V> map; public void someMethod1 () { // operation 1 on map // operation 2 on map } public void someMethod2 () { // operation 3 on map // operation 4 on map } } 

Now I do not need synchronization of operations in "someMethod1" or "someMethod2". This means that if there are two threads calling "someMethod1" at the same time, I do not need to serialize these operations (because ConcurrentMap will do the job).

But I hope that "someMethod1" and "someMethod2" are mutual characters of each other, which means that when some thread executes "someMethod1", the other thread must wait for the input of "someMethod2" (but the other thread should be allowed to enter " someMethod1 "").

So, in short, is there a way I can make "someMethod1" and "someMethod2" not a mutex, but a mutex of each other?

I hope I have set out my question in sufficient detail ...

Thanks!

+7
source share
5 answers

I tried a couple of attempts with higher-level designs, but nothing came to mind. I think this may be the reason for moving to the low-level API:

EDIT: I really think you are trying to fix a problem that is intrinsically complex (see the second to last paragraph) and probably not needed (see the last paragraph). But that said how this can be done, and I will leave a color comment for the end of this answer.

 private int someMethod1Invocations = 0; private int someMethod2Invocations = 0; public void someMethod1() { synchronized(this) { // Wait for there to be no someMethod2 invocations -- but // don't wait on any someMethod1 invocations. // Once all someMethod2s are done, increment someMethod1Invocations // to signify that we're running, and proceed while (someMethod2Invocations > 0) wait(); someMethod1Invocations++; } // your code here synchronized (this) { // We're done with this method, so decrement someMethod1Invocations // and wake up any threads that were waiting for that to hit 0. someMethod1Invocations--; notifyAll(); } } public void someMethod2() { // comments are all ditto the above synchronized(this) { while (someMethod1Invocations > 0) wait(); someMethod2Invocations++; } // your code here synchronized(this) { someMethod2Invocations--; notifyAll(); } } 

One glaring problem with the above is that it can lead to thread starvation . For example, someMethod1() works (and blocks someMethod2() s), and as soon as it finishes, another thread appears and calls someMethod1() . This happens very well, and just as another thread ends, someMethod1() starts, etc. In this case, someMethod2() will never get a chance to run. This is actually not an error in the code above; this is a problem with your design needs that should actively work on the solution. I think an honest AbstractQueuedSynchronizer could do the trick, although this exercise is left to the reader. :)

Finally, I can't resist, but embed an opinion: given that ConcurrentHashMap operations are pretty simple, you could better just put one mutex around both methods and just do it. Therefore, yes, threads will have to queue to call someMethod1() , but each thread will complete its turn (and thus allow other threads to continue) very quickly. This should not be a problem.

+4
source

Perhaps this will not work (see comments) - leaving it for information.


One way would be to use Semaphores :

  • one semaphore sem1 , with one resolution associated with method1
  • one sem2 semaphore with one resolution associated with method2

when entering method1, try to get sem2 permission and, if available, immediately release it.

See this post for an example implementation.

Note: in your code, even if ConcurrentMap is thread safe, operation 1 and operation 2 (for example) are not atomic, so the following interleaving is possible in your script:

  • Topic 1 launches operation 1
  • Thread 2 launches operation 1
  • Thread 2 launches operation 2
  • Thread 1 starts operation 2
+2
source

I think this should work

 class A { Lock lock = new Lock(); private static class Lock { int m1; int m2; } public void someMethod1() throws InterruptedException { synchronized (lock) { while (lock.m2 > 0) { lock.wait(); } lock.m1++; } // someMethod1 and someMethod2 cannot be here simultaneously synchronized (lock) { lock.m1--; lock.notifyAll(); } } public void someMethod2() throws InterruptedException { synchronized (lock) { while (lock.m1 > 0) { lock.wait(); } lock.m2++; } // someMethod1 and someMethod2 cannot be here simultaneously synchronized (lock) { lock.m2--; lock.notifyAll(); } } } 
+2
source

First of all . Your card is thread safe like its ConcurrentMap. This means that operations on this card, such as add, contains, etc., are thread safe.

Secondaly This does not guarantee that even your methods (somemethod1 and somemethod2) are also thread safe. Thus, your methods are not mutually exclusive, and two threads can simultaneously access them.

Now you want them to be mutual symbols of each other . One approach would be to put all the operations (operator 1, .. operation 4) in one method and based on calling the conditions each.

0
source

I think you cannot do this without a special synchronizer. I hacked it, I called it TrafficLight , because it allows you to skip streams with a certain state, stopping others until it changes state:

 public class TrafficLight<T> { private final int maxSequence; private final ReentrantLock lock = new ReentrantLock(true); private final Condition allClear = lock.newCondition(); private int registered; private int leftInSequence; private T openState; public TrafficLight(int maxSequence) { this.maxSequence = maxSequence; } public void acquire(T state) throws InterruptedException { lock.lock(); try { while ((this.openState != null && !this.openState.equals(state)) || leftInSequence == maxSequence) { allClear.await(); } if (this.openState == null) { this.openState = state; } registered++; leftInSequence++; } finally { lock.unlock(); } } public void release() { lock.lock(); try { registered--; if (registered == 0) { openState = null; leftInSequence = 0; allClear.signalAll(); } } finally { lock.unlock(); } } } 

acquire() will block if another state is active until it becomes inactive.

maxSequence helps prevent streaming starvation by allowing only the maximum number of threads to be skipped (then they will have to queue, like the rest). You can make an option that uses a time window.

For your problem, someMethod1() and someMethod2() will call the gets () method with a different state at the beginning and release() at the end.

0
source

All Articles