-1

I am going to parallelize some code with some global variables. I am going to use ReentrantReadWriteLock. Did I understand it right, that I need one own instance of ReentrantReadWriteLock per variable I want to make thread safe?

I mean, when I have two lists where every thread can attach an item and all threads are sometimes reading items from that lists. In that case I would implement something like:

private static String[] globalVariables = null;
private static String[] processedItems = null;

private final ReentrantReadWriteLock globalVariablesLock = new ReentrantReadWriteLock();
private final Lock globalVariablesEeadLock  = globalVariablesLock .readLock();
private final Lock globalVariablesWriteLock = globalVariablesLock .writeLock();
private final ReentrantReadWriteLock processedItemsLock = new ReentrantReadWriteLock();
private final Lock processedItemsLockReadLock  = processedItemsLock .readLock();
private final Lock processedItemsLockWriteLock = processedItemsLock .writeLock();

What if I have much more variables like databaseconnection(pool)s, loggers, further lists, etc.

Do I need to make a new ReentrantReadWriteLock or do I missing something? Samples on the internet only handles one variable.

Thanks in advance.

ComicSansMS
  • 51,484
  • 14
  • 155
  • 166
Kaspatoo
  • 1,223
  • 2
  • 11
  • 28

3 Answers3

1

What are you trying to protect?

Don't think of locking variables. The purpose of a lock is to protect an invariant. An invariant is some assertion that you can make about the state of your program that must always be true. An example might be, "the sum of variables A, B, and C will always be zero."

In that case, it doesn't do you any good to have separate locks for A, B, and C. You want one lock that protects that particular invariant. Any thread that wants to change A, B, or C must lock that lock, and any thread that depends on their sum being zero must lock that same lock.

Often it is not possible for a thread to make progress without temporarily breaking some invariant. E.g.,

A += 1;    //breaks the invariant
B -= 1;    //fixes it again.

Without synchronization, some other thread could examine A, B, and C in-between those two statements, and find the invariant broken.

With synchronization:

private final Object zeroSumLock = new Object();

void bumpA() {
    synchronized(zeroSumLock) {
        A += 1;
        B -= 1;
    }
}

boolean verifySum() {
    synchronized(zeroSumLock) {
        return (A+B+C) == 0;
    }
}
Solomon Slow
  • 25,130
  • 5
  • 37
  • 57
  • One use is a list of pre compiled sql statements where the lists is used as cache. The list is implemented as an projectspecific object, so its not just a hasmap which I could turn into a concurrent equivalent. The threads shall access this list, take a specific sql, prepare it, set some variables on it and run it on db. Also some threads access it and dont find the sql they need. So they compile it (from another project specific, sql like language) on runtime and add it to the cache. For this purpose I think reentrantreadwritelock is a good technique. There are some more objects like this. – Kaspatoo Oct 21 '15 at 16:10
  • @Kaspatoo what you just described is definitely invariant: by lock you're trying to presreve data integrity, so no thread could see the list in state when items have patially filled properties and so on. – Alex Salauyou Oct 22 '15 at 04:50
  • @Kaspatoo if you're creating your own cache classes, why don't you encapsulate locks in those classes? Common practice for such tasks is to compose API with methods that are executed atomically from caller's point of view (e. g. `ConcurrentMap#putIfAbsent()`), keeping locking mechanism strongly encapsulated. – Alex Salauyou Oct 22 '15 at 04:56
  • I am not able to change those classes because I am not the owner of them and they are not going to change them since they would need to change any existing interface-agreement with other teams – Kaspatoo Oct 22 '15 at 06:45
  • soo your answer to my question is to use a getter to my objects and synchronize it but the advantage of using reentrantreadwritelock is that concurrent reads are possible but only one write thats why I am not going to synchronize – Kaspatoo Oct 22 '15 at 06:47
  • @Kaspatoo "locking mechanizm" which I'm talking about may be any, not necessarily using synchronized blocks or methods. I'm talking about encapsulation, otherwise you'll need to store that bunch of locks somewhere in your code. The very big problem with this is that your team's code, which also can use those cache objects, may have no idea about synchronization you implemented, or may use their own, which will lead to breaking data integrity. If you're using common contract, define synchronization policy there, otherwise you'll come with poorly designed and erroneous product. – Alex Salauyou Oct 22 '15 at 09:15
0

Yes, you should have one Lock per thread-safe variable (arrays in your case). However, consider using

ArrayList<String> syncList = Collections.synchronizedList(new ArrayList<String>());

instead of arrays. It is usually way better when you delegate to the library (in this case, not only the synchronization but also the resize of the arrays). Of course, before doing it check that the library does exactly what you would expect (in this case, as @SashaSalauyou pointed out, you'd lose the ability to read concurrently).

francesco foresti
  • 2,004
  • 20
  • 24
0

One of solutions is creating an immutable Map, where you put locks for all items you need:

final static Map<String, ReadWriteLock> locks = Collections.unmodifiableMap(
     new HashMap<String, ReadWriteLock>() {{
         put("globalVariables", new ReentrantReadWriteLock());
         put("processedItems",  new ReentrantReadWriteLock());
         // rest items
     }}
);

As HashMap is wrapped by Collections.unmodifiableMap() and thus cannot be modified, it becomes thread-safe.

Then, in code:

Lock lo = locks.get("globalVariables").readLock();
lo.acquire();
try {
    // ...
} catch (Exception e) {
    // ...
} finally {
    lo.release();
}
Alex Salauyou
  • 14,185
  • 5
  • 45
  • 67
  • I dont think that this would reduce anything, and additionally I would need to make constants not to have hard coded text to get the right lock my question was more intendet to ask whether I really would need to make an own lock or if I missunderstood the concept – Kaspatoo Oct 21 '15 at 16:01