Déjà vu
Déjà vu

Reputation: 28840

Java consistent synchronization

We are facing the following problem in a Spring service, in a multi-threaded environment:

My initial idea is to make a container object having the three lists as properties.
Then the synchronization would be first on that object, then one by one on each of the three lists.

Some code is worth a thousands words... so here is a draft

    private class Sync {
        final List<Something> a = Collections.synchronizedList(new ArrayList<Something>());
        final List<Something> b = Collections.synchronizedList(new ArrayList<Something>());
        final List<Something> c = Collections.synchronizedList(new ArrayList<Something>());
    }

    private Sync _sync = new Sync();

   ...

   void updateRunOnceEveryFiveMinutes() {
     final List<Something> newa = new ArrayList<Something>();
     final List<Something> newb = new ArrayList<Something>();
     final List<Something> newc = new ArrayList<Something>();

     ...building newa, newb and newc...

     synchronized(_sync) {

        synchronized(_sync.a) {
            _synch.a.clear();
            _synch.a.addAll(newa);
        }

        synchronized(_sync.b) { ...same with newb... }

        synchronized(_sync.c) { ...same with newc... }
   }

   // Next is accessed by clients

   public List<Something> getListA() {
      return _sync.a;
   }

   public List<Something> getListB() { ...same with b... }

   public List<Something> getListC() { ...same with c... }

The question would be,

update

Changed the order of _sync synchronization and newa... building.

Thanks

Upvotes: 1

Views: 435

Answers (3)

jtahlborn
jtahlborn

Reputation: 53694

your best option is to expose a "current state" object and use volatile, ditching all the synchronized code. (also, you may want to make the lists unmodifiable wrappers just to be safe).

public class ListState {
  List final a;
  List final b;
  List final c;
}

private volatile ListState _state;

void updateRunOnceEveryFiveMinutes() {
  // generate new lists ...

  // re-assign current list state (e.g. atomically publish all your updates)
  _state = new ListState(newA, newB, newC);
}

public ListState getCurrentLists() {
  return _state;
}

Upvotes: 2

user207421
user207421

Reputation: 310936

The draft doesn't accomplish your objective. There is nothing here that prevents access to the old C while the new A or B are being computed. The simplest way to accomplish that is like so:

synchronized (a) {
  synchronized (b) {
    synchronized (c) {
    }
  }
}

around all accesses whether read or write. And I wouldn't do clear() and addAll(), just change the variable value to newA/newB/newC, although that would require some adjustment to the above.

Upvotes: 1

finnw
finnw

Reputation: 48629

It's a bit wasteful to build a new list only to copy it into another (final) list.

Consider using immutable lists and assigning the new list to _sync.a, _sync.b and _sync.c (having removed the final modifier from them) after building the list (in updateRunOnceEveryFiveMinutes.)

Also you are synchronizing on 4 different objects. It would be simpler to synchronize on just one object (_sync or possibly a dedicated lock object) for each method call.

If the lists returned by getListA etc. are immutable you do also not need the calls to Collections.synchronizedList.

Upvotes: 1

Related Questions