简体   繁体   中英

Why is this code not thread-safe, even when using a synchronized method?

Why is this code not thread-safe even though we are using synchronized method and hence obtaining a lock on Helper object?

class ListHelper <E> {
    public List<E> list = Collections.synchronizedList(new ArrayList<E>());

    public synchronized boolean putIfAbsent(E x) {
        boolean absent = !list.contains(x);
        if (absent)
            list.add(x);
        return absent;
    }
}

Because the list is unlocked when contains returns, and then locked again when add is called. Something else could add the same element between the two.

If you mean to only use the list from within the helper object, it should be declared private ; if you do this, the code will be thread safe, as long as all manipulations of the list go through methods that are synchronized in the helper object. It's also worth noting that as long as this is the case, you don't need to be using a Collections.synchronizedList as you're providing all necessary synchronization in your own code.

Alternatively, if you want to allow the list to be public, you need to synchronize your access on the list, rather than on your helper object. The following would be thread safe:

class ListHelper <E> {
    public List<E> list = Collections.synchronizedList(new ArrayList<E>());

    public boolean putIfAbsent(E x) {
        synchronized (list) {
            boolean absent = !list.contains(x);
            if (absent)
               list.add(x);
            return absent;
        }
    }
}

The difference is that it is using the same lock as the other methods of the list, rather than a different one.

This code is not thread safe only because list is public.

If the list instance is private, and referenced nowhere else, this code is threadsafe. Else it is not threadsafe as multiple threads could be manipulating the list simultaneously.

If the list is not referenced elsewhere, you need not declare it as a synchronized list through the collections class, as long as all list manipulation occurs through synchronized methods and a reference to that list is never returned to anything.

When you mark a method synchronized, all threads calling that method are synchronized with the object instance said method is defined in. This is why if ListHelper internal list instance is not referenced elsewhere, and all methods are synchronized, your code would be threadsafe.

A major component of thread safety concerns more than only mutual exclusion. It is quite possible to complete an atomic update of an object's state, ie to effect a state transition that leaves an object in a valid state with its invariants intact, but to still leave the object vulnerable if its references are still published to untrustworthy or incompletely debugged clients.

In the example you post:

public synchronized boolean putIfAbsent(E x) {
    boolean absent = !list.contains(x);
    if (absent)
        list.add(x);
    return absent;
}

The code is thread safe, as WM pointed out. But we have no assurances about x itself and where it may have references still held by other code. If such references did exist, another thread can modify corresponding elements in your list, defeating your efforts to guard the invariants of objects in the list.

If you are accepting elements to this list from client code that you don't trust or don't know about, a good practice would be to make a defense copy of x and then add that to your list. Similarly, if you will be returning an object from your list to other client code, making a defensive copy and returning that will help assure that your list remains thread safe.

Moreover, the list should be fully encapsulated in the class. By having it be public, client code anywhere can freely access the elements and make it impossible for you to protect the state of objects in the list.

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