6

I have an object which is a singleton. This object declares:

List<Player> players = new ArrayList<Player>();

The same object also specifies 4 operations on this arrayList:

public List<Player> getPlayers() {
return players;
} // the result of this method can be used in another object as an iterator (or maybe by index-access)

and

public void removePlayer(Player player) {
players.remove(player);
}

public void addPlayer(Player player) {
players.add(player);
}

public boolean isPresent(Player player) {
if (players.constans(player)) {...
}

Right now in the constructor I am doing it like that:

players = Collections.synchronizedList(new ArrayList<Player>());

But what is the CORRECT way to synchronize these methods. It seems like if I use iterator in another class, it will still through the concurrent modification exception. Does the exception happen if a 2 threads call at the same time the "remove" and "contains" method? There are many threads to access the singleton so I would like to know the method to do this with the minimum hit on performance.

5
  • 1
    As explained by tieTYT, every access to the list must be synchronized. This is an utopic goal. So you shouldn't let anyone access the list from outside of your class. Or you should use a concurrent list instead. Commented Jul 10, 2013 at 18:36
  • Does the concurrent use of "contains" and "remove" throw an exception? Commented Jul 10, 2013 at 18:37
  • 1
    If acceptable, you can return copy of list, you can even return unmodifiable copy of list to make sure that any change will fail with exception. For the guys who think that they can add objects without using your API. Commented Jul 10, 2013 at 18:38
  • 2
    No, because these methods are synchronized. But as soon as you iterate, or do a check-then-act operation such as if (!list.contains(foo)) list.add(foo), you need explicit synchronization. Also beware of hidden iterations such as in new ArrayList<>(list). Commented Jul 10, 2013 at 18:39
  • For other options see stackoverflow.com/questions/207829/… Commented Jul 10, 2013 at 18:53

2 Answers 2

16

The documentation answers your question.

It is imperative that the user manually synchronize on the returned list when iterating over it:

List list = Collections.synchronizedList(new ArrayList());
      ...
  synchronized(list) {
      Iterator i = list.iterator(); // Must be in synchronized block
      while (i.hasNext())
          foo(i.next());
  }

As for contains and remove, you shouldn't have to synchronize manually. I'm looking at the source code of Collections and it looks like it does that for you:

    public boolean contains(Object o) {
        synchronized (mutex) {return c.contains(o);}
    }
    public boolean remove(Object o) {
        synchronized (mutex) {return c.remove(o);}
    }

It wouldn't be a synchronized list if you have to do this stuff on your own.

Sign up to request clarification or add additional context in comments.

2 Comments

Pardon my ignorance but then what is the value-add of 'Collections.synchronizedList'? If you synchronize on the original list then doesn't this solve all the problems?
@TimCooper in the documenation it's stated Iterator i = list.iterator(); // Must be in synchronized block. And yes, my +1 to your question, it sounds logical that synchronizedList is expected to do the job
6

If you don't plan on updating it often use a CopyOnWriteArrayList otherwise @tieTYT's suggestion works.

You can also manage this yourself by returning a copy of the list instead of the actual list within the getPlayers. (If you are using Collections.synchronizedList)

public List<Player> getPlayers(){
    synchronized(list){
       Collections.unmodifiableList(new ArrayList<Player>(list));
    }
}

This has the side effect of a list being out of date after an add/remove is done

Edit: Stealing Grzegorz suggestion for unmodifiableList

1 Comment

wouldn't it be out of date after u synchronized it anyway?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.