Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Concurrency: ReadWriteLock on Variable

I am looking for a way to combine functionality from synchronized with functionality from java.util.concurrent.locks.ReentrantReadWriteLock. More specifically, I would like one method to lock only if the WriteLock is set, but I want the lock to be on an object variable like synchronized. Here are examples that will help explain:

ReentrantReadWriteLock Example

public void methodOne(String var) {
    lock.writeLock().lock;
    try {
        // Processing
    }
    finally {
        lock.writeLock().unlock();
    }
}

public void methodTwo(String var) {
    lock.readLock().lock;
    try {
        // Processing
    }
    finally {
        lock.readLock().unlock();
    }
}

In this example, methodTwo can be invoked without blocking as long as the writeLock on methodOne has not been locked. This is good and the functionality I'm looking for, however when the writeLock is locked, it is locked for all cases of var. Instead, I would like for it only to lock on var like with synchronized:

Synchronized Example

public void methodOne(String var) {
    synchronized(var) {
        // Processing
    }
}

public void methodTwo(String var) {
    synchronized(var) {
        // Processing
    }
}

Now, the methods will only block conditional on the input. This is good and and what I'm looking for, however if there are multiple concurrent calls to methodTwo with the same key, they will block. Instead, I would like this to be a "read" style lock and allow the call.

Essentially, I would like a ReadWriteLock that I can synchronize to a variable to obtain both sets of functionality.

like image 316
lamarvannoy Avatar asked Oct 23 '13 21:10

lamarvannoy


2 Answers

Essentially, I would like a ReadWriteLock that I can synchronize to a variable to obtain both sets of functionality.

First off, depending on the number different values, this might create some huge number of locks. You might consider having some sort of classification or something so you can partition the updates to a small number instead. For example, is there a way you can subdivide the different var values. Maybe there are various types of values? If there is a small fixed number of values then this won't be a problem.

You are going to have to build some sort of collection of ReentrantReadWriteLock. Maybe use a ConcurrentHashMap which will store all of the locks.

Map<String, ReadWriteLock> lockMap =
    new ConcurrentHashMap<String, ReadWriteLock>():
...
public void writeProcessing(String var) {
    lock = lockMap.get(var);
    lock.writeLock().lock;
    try {
       // Processing
    } finally {
       lock.writeLock().unlock();
    }
}

You will need to register the different values beforehand. Or you can create them on demand. Again, this might create some huge number of locks which may be prohibitive.

public void writeProcessing(String var) {
    lock = getLockForVar(var);
    ...
}

private ReadWriteLock getLockForVar(String var) {
   ReadWriteLock lock = lockMap.get(var);
    if (lock != null) {
        return lock;
    }
    // this might create an extra lock because of race conditions...
    lock = new ReadWriteLock();
    ReadWriteLock current = lockMap.putIfAbsent(var, lock);
    if (current == null) {
        return lock;
    } else {
        return current;
    }
}
like image 126
Gray Avatar answered Sep 23 '22 07:09

Gray


You could also use some kind of wrapper, if feasible. So will have a unique lock for each wrapped object.

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;


public abstract class ThreadSafeWrapper<T> {

  private final ReadWriteLock lock = new ReentrantReadWriteLock();
  private final T object;

  public ThreadSafeWrapper(T object) {
    this.object = object;
  }

  public final void methodOneWriteLock(String var) {
    lock.writeLock().lock();
    try {
      doMethodOne(object, var);
    } finally {
      lock.writeLock().unlock();
    }
  }

  public final void methodTwoReadLock(String var) {
    lock.readLock().lock();
    try {
      doMethodTwo(object, var);
    } finally {
      lock.readLock().unlock();
    }
  }

  protected abstract void doMethodOne(T obj, String var);

  protected abstract void doMethodTwo(T obj, String var);

}

Could be used like that:

public class SampleWrapper {

  public static void main(String[] args) {

    ThreadSafeWrapper<String> wrapper = new ThreadSafeWrapper<String>("lala") {

      @Override
      protected void doMethodOne(String obj, String var) {
        System.out.println("method one: " + obj + " " + var);
      }

      @Override
      protected void doMethodTwo(String obj, String var) {
        System.out.println("method two: " + obj + " " + var);
      }
    };

    wrapper.methodOneWriteLock("foo");
    wrapper.methodTwoReadLock("bar");

  }

}

You could do something similar with interfaces. Its just to point you in some direction.

like image 37
Ortwin Angermeier Avatar answered Sep 22 '22 07:09

Ortwin Angermeier