Search code examples
javaatomicconcurrenthashmap

Check Map value, wait if need and update atomically


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<>();
  1. Several tasks are executed in different threads.
  2. Each task checks whether resources are available before execution. If not, it waits until they are released by another task, otherwise it updates the counter, thereby temporarily taking the necessary resources for itself. This code fragment must be executed atomically.
  3. After the necessary calculations, the task "releases" the resources and decrements the counter.
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());
        }
}

Solution

  • 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