简体   繁体   中英

Should I synchronize my method?

I have defined a Synchronized ArrayList which will be accessed by multi-threads.

List<Object> objList = Collections.synchronizedList(new ArrayList<Object>());

I want to define few custom operations like add , remove , edit , delete for the above list.

Should my operations need to be synchronized ?
As I have already made my List synchronizedList , wouldn't that be enough to make sure the atomic operations?

Thanks!

If you define custom operations, those will not inherit the synchronizations from Collections.synchronizedList() (the currently synchronized methods will remain synced).

Think of it this way: if you have a thread-safe class, which you then extend, the thread safety does not also extend into new methods in the new class. Thread safety and synchronization only apply to the methods which are inherited, not to custom definitions.

Therefore, yes, if you want your new methods to be synchronized, you must explicitly declare them synchronized .

The question as asked doesn't make sense. You're asking about adding methods to an object, not to a class -- you can't do that in Java. Collections.synchronizedList chooses the List's class -- it's either java.util.Collections.SynchronizedRandomAccessList or ...SynchronizedList . Furthermore, each of those is package-private, so you can't extend them.

But let's say you did have an implementation similar to what those provide:

public class SyncedList<E> implements List<E> {
    private final List<E> delegate;

    public SyncedList(List<E> delegate) { this.delegate = delegate; }

    @Override
    public synchronized boolean add(E e) {
        return delegate.add(e);
    }

    @Override
    public synchronized void clear() {
        delegate.clear();
    }

    // ... etc

Now you extend this class:

public class MySyncedList<E> extends SyncedList<E> {
    ...

Your question would now be: what do we have to do with the overriden methods? And the answer is... it depends!

The general rule to remember is that overriden methods do not inherit their super's synchronized modifier. But you may not need it. Here are some scenarios:

Scenario 1: don't invoke super methods at all

Example:

@Override
public void clear() {
    throw new UnsupportedOperationException();
}

In this case, it should be clear (pun alert!) that you don't need any synchronization, since there's nothing to synchronize on. (Trivial example, but easy to get out of the way.)

Scenario 2: invoke one super method without touching other mutable state

Example:

private final E dontAllow; // assume this is set in the constructor
@Override
public boolean add(E e) {
    if (Objects.equals(e, dontAllow))
        throw new IllegalArgumentException("can't add element: " + e);
    super.add(e);
}

In this case, you don't actually need to synchronize add . The call to super.add(e) will retain its synchronization, and you haven't touched any other mutable state, so there's nothing extra to synchronize on.

Scenario 3: invoke one super method but also touch mutable state

Example:

private E lastAdded = null;

@Override
public synchronized boolean add(E e) {
    boolean result = super.add(e); // do this first in case it throws an exception
    lastAdded = e;
    return result;
}

public synchronized E getLastAdded() {
    return lastAdded;
}

Here we've added additional state, so we have to synchronize on it! Note that in this particular case, you could have also made lastAdded volatile and not made either method synchronized -- but in other cases, that may not be possible.

Scenario 4: invoke more that one method on super

Example:

public synchronized void add(E e) {
    if (indexOf(e) >= 0)
        return false;
    return super.add(e);
}

Here you do need synchronization, assuming you care about atomicity. It's not inherited from super.add . Each of the calls within the method ( indexOf and super.add ) are synchronized, but you need additional atomicity.

(To be a bit more precise, you need to invoke both of the methods while synchronized on a monitor, which should also be the monitor that each of the methods use. In this case, those two methods are synchronized, so that monitor is just this , which is what a synchronized method synchronizes on.)

Final thoughts

Even if you don't have to synchronize an overriden method, it may be a good idea to if it reads or writes state (eg scenario 2 above, and 3 if you went with the volatile lastAdded approach). All of the other mutator/accessor methods are synchronized, so your API will look weird if one method is not. People will ask, "hey, shouldn't add be synchronized?" and you'll have to convince them it doesn't need to be. What's more, you won't actually gain anything, because that method will eventually synchronize anyway, when it hits the super call.

I haven't covered all possible scenarios in the above, but hopefully that gives you a sense of how things work.

Take a look at java.util.concurrent.CopyOnWriteArrayList . It provides solid out of the box synchronization in a scenario where you don't need to do a lot of mutations.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM