简体   繁体   中英

Java volatile variable question

Reading this DZone article about Java concurrency I was wondering if the following code:


    private volatile List list;
    private final Lock lock = new ReentrantLock();

    public void update(List newList) {
        ImmutableList l = new ImmutableList().addAll(newList);
        lock.lock();
        list = l;
        lock.unlock();
    }

    public List get() {
        return list;
    }

is equivalent to:


    private volatile List list;

    public void update(List newList) {
        ImmutableList l = new ImmutableList().addAll(newList); 
        list = l;
    }

    public List get() {
        return list;
    }

The try { } finally { } block was omitted for brevity. I assume the ImmutableList class to be a truly immutable data structure that holds its own data, such as the one provided in the google-collections library. Since the list variable is volatile and basically what's going on is a copy-on-the-fly, isn't it safe to just skip on using locks?

In this very specific example, I think you would be OK with no locking on the variable reassignment.

In general, I think you are better off using an AtomicReference instead of a volatile variable as the memory consistency effects are the same and the intent is much clearer.

Yes, both of those code samples behave the same way in a concurrent environment. Volatile fields are never cached thread-locally , so after one thread calls update(), which replaces the list with a new list, then get() on all other threads will return the new list.

But if you have code which uses it like this:

list = get()
list = list.add(something) // returns a new immutable list with the new content
update(list)

then it won't work as expected on either of those code examples (if two threads do that in parallel, then the changes made by one of them may be overwritten by the other). But if only one thread is updating the list, or the new value does not depend on the old value, then no problem.

重新阅读之后,这些是等效的。

If we are talking about timing and memory visibility. A volatile read is very close to the time it takes to do a normal read. So if you are doing get() alot then there is little difference. The time it takes to do a volatile write is about 1/3 time to acquire and release a lock. So your second suggestion is a bit faster.

The memory visibility as most people suggested is equivalent, that is any read before the lock acquisition happens before any write after the lock acquisition similar to any read before a volatile read happens before any subsequent write

The following criteria must be met for volatile variables to provide the desired thread-safety:

  1. Writes to the variable do not depend on its current value.
  2. The variable does not participate in invariants with other variables.

Since both are met here - code is thread-safety

I think the default synchronization behavior of volatile doesn't guarantee the ReentrantLock behavior, so it might help with performance. Otherwise, I think it's fine.

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