1

I have a simple requirement where a resource (let's say a linked-list) is shared between two services:one adds elements to it and another one calculates statistics of it. I want to use re-entrant locks in java. I have come up with following solution.

Is there any better solution than this?

public class SharedServer {
    private List<Integer> numbers;

    public SharedServer(List<Integer> numbers){
        this.numbers = numbers;
    }

    Lock lock = new ReentrantLock();

    public void addElements(int element){
        try{
            Thread.sleep(100);
            System.out.println("Adding element");
            numbers.add(element);
            System.out.println("Added : "+element);
        }catch(InterruptedException e){
            System.out.println("Interrupted while adding elements");
        }  
    }

    public void caluclateStatistics(){
        try{
            Thread.sleep(200);
            System.out.println("calculating statistics");
            System.out.println("Min : "+Collections.min(numbers)+" Max :      "+Collections.max(numbers)+" Avg : "+(Collections.min(numbers)+Collections.max(numbers))/numbers.size());
        }catch(InterruptedException e){
            System.out.println("Interrupted while performing calculations on elements");
        }
   }

}

public class StatisticsCalculator implements Runnable {

    private SharedServer sharedServer;

    public StatisticsCalculator(SharedServer sharedServer){
        this.sharedServer = sharedServer;
    }

    @Override
    public void run() {
        System.out.println("Calculator");
        boolean acquired = false;
        try {
            acquired = sharedServer.lock.tryLock(300,TimeUnit.MILLISECONDS);
            sharedServer.caluclateStatistics();
        } catch (InterruptedException e) {
            System.out.println("COULD NOT ACQUIRE CALCULATOR");
            e.printStackTrace();
        }finally{
            if(acquired){
                sharedServer.lock.unlock();
                System.out.println("Calculator released");
            }
        }

  }
}


public class ElementAdder implements Runnable {

    private SharedServer sharedServer;

    public ElementAdder(SharedServer sharedServer){
        this.sharedServer = sharedServer;
    }

    @Override
    public void run() {
        System.out.println("Adder");
        boolean acquired = false;
        try {
            acquired = sharedServer.lock.tryLock(300,TimeUnit.MILLISECONDS);
            sharedServer.addElements(ThreadLocalRandom.current().nextInt(1, 1000));
        } catch (InterruptedException e) {
            System.out.println("COULD NOT ACQUIRE ADDER");
            e.printStackTrace();
        }finally{
            if(acquired){
                sharedServer.lock.unlock();
                System.out.println("Adder released");
            }
        }
     }

}


public class Main {

    public static void main(String[] args) {
        List<Integer> ints = new ArrayList<>();
        SharedServer sharedService = new SharedServer(ints);
        ExecutorService executorService1 = Executors.newCachedThreadPool();
        ExecutorService executorService2 = Executors.newCachedThreadPool();
        for(int index=0; index<50;index++){
            executorService1.submit(new ElementAdder(sharedService));
        }
        for(int index=0; index<50;index++){
            executorService2.submit(new StatisticsCalculator(sharedService));
        }
        executorService1.shutdown();
        executorService2.shutdown();
    }

 }

Only mandatory thing is no call (adder or calculator) should not miss.

Arun Rahul
  • 565
  • 1
  • 7
  • 24
  • What do you really need to configure here: the timeout on the lock acquiring, the method of the lock acquiring (`tryLock`/`lock`) or something else? If none of these, then why are you exposing the lock? – user3707125 Feb 07 '16 at 13:53
  • I can kee the lock private and comletely take off it from ElementAdder and StatisticsCalculator. But, my question is, is this the correct approach – Arun Rahul Feb 08 '16 at 01:17
  • That depends on the perspective. If it is a code of some real product, which potentially may be accessed by other developers, then this approach is a no-go: your class's users must be aware of some internal specifics, and this approach is very error prone. If it is a code for one-time use, and you will never-ever return back to it, then it may be a plausible solution. Also you didn't answer any of relevant questions (the "what do you really need to configure here" part). – user3707125 Feb 08 '16 at 10:48

1 Answers1

1

Do you have a specific reason to use a reentrant lock? Does your calculateStatistics() method do IO? (not sure if your example is over-simplified)

Generally, synchronized is conceptually simpler and easier to get right, so unless you need the timeout (or other features of a ReentrantLock) you might want to consider just using synchronized.

leeor
  • 17,041
  • 6
  • 34
  • 60