I'm looking for an easy to understand atomic replacement for the code snippet below. Or an algorithm on how to do it correctly.
Let's assume there is a map:
// key->number of resources occupied
Map<Long, Integer> map = new ConcurrentHashMap<>();
private void do(long key, int count) {
// must be atomic code
while (map.get(key) + count > 25) {
sleep(100);
}
map.put(key, map.get(key) + count);
// must be atomic code
// do some computations
// atomic analog of map.put(key, map.get(key) - count);
map.merge(key, count, (current, next) -> current - next);
}
I experimented with ConcurrentHashMap
methods, specifically tried to sleep in the merge
method, but the code hangs at the moment of executing the final code string map.merge(key, count, (current, next) -> current - next)
.
Example of code:
map.merge(key, count, (current, next) -> {
while (current + next > 25) {
sleep(100);
current = map.get(key);
}
return current + next;
}
);
EDIT1
I tried the Semaphore class. Please comment if I am using it correctly?
Map<Long, Semaphore> map = new ConcurrentHashMap<>();
private void do(long key, int count) {
Semaphore semaphore = map.computeIfAbsent(key, s -> new Semaphore(5));
semaphore.acquire(count);
try {
// do some computations
semaphore.release(count);
} finally {
semaphore.release(intervals.size());
}
}
in this simple example only one thread can be executing the critical section because we use Semaphore with one permit Semaphore sem = new Semaphore(1)
Let's use the semaphore:
import java.util.concurrent.Semaphore;
public class Program {
public static void main(String[] args) {
Semaphore sem = new Semaphore(1); // 1 permit
CommonResource res = new CommonResource();
new Thread(new CountThread(res, sem, "CountThread 1")).start();
new Thread(new CountThread(res, sem, "CountThread 2")).start();
new Thread(new CountThread(res, sem, "CountThread 3")).start();
}
}
class CommonResource{
int x=0;
}
class CountThread implements Runnable{
CommonResource res;
Semaphore sem;
String name;
CountThread(CommonResource res, Semaphore sem, String name){
this.res=res;
this.sem=sem;
this.name=name;
}
public void run(){
try{
System.out.println(name + " wait permit");
sem.acquire(); //here start critical section add your logic below
res.x=1;
for (int i = 1; i < 5; i++){
System.out.println(this.name + ": " + res.x);
res.x++;
Thread.sleep(100);
}
}
catch(InterruptedException e){System.out.println(e.getMessage());}
System.out.println(name + " release permit");
sem.release();
}
}
console output:
CountThread 1 wait permit
CountThread 2 wait permit
CountThread 3 wait permit
CountThread 1: 1
CountThread 1: 2
CountThread 1: 3
CountThread 1: 4
CountThread 1 release permit
CountThread 3: 1
CountThread 3: 2
CountThread 3: 3
CountThread 3: 4
CountThread 3 release permit
CountThread 2: 1
CountThread 2: 2
CountThread 2: 3
CountThread 2: 4
CountThread 2 release permit