javaconcurrencylockingvolatilereadwritelock

Java readwrite concurrency: readwrite lock vs lock + volatile


Assuming that readers = 10 x writers, which of the following solutions is better in terms of throughput. Which solution is better to use in production code?

  1. Using single lock for set operation and volatile variable for get operation
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public final class InstanceProvider {

    private final Lock wLock = new ReentrantLock();
    private volatile List<Instance> instances;

    public void setInstances(List<Instance> newInstances) {
        wLock.lock();
        try {
            doSmthWith(newInstances);
            instances = newInstances;
        } finally {
            wLock.unlock();
        }
    }

    public List<Instance> getInstances() {
        return instances;
    }

    @Getter
    public static final class Instance {

        private final String name;

        public Instance(String name) {
            this.name = name;
        }

    }
}
  1. Using single read-write lock for both set and get operations
import java.util.List;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public final class InstanceProvider {

    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private List<Instance> instances;

    public void setInstances(List<Instance> newInstances) {
        lock.writeLock().lock();
        try {
            doSmthWith(newInstances);
            instances = newInstances;
        } finally {
            lock.writeLock().unlock();
        }
    }

    public List<Instance> getInstances() {
        lock.readLock().lock();
        try {
            return instances;
        } finally {
            lock.readLock().unlock();
        }
    }

    @Getter
    public static final class Instance {

        private final String name;

        public Instance(String name) {
            this.name = name;
        }

    }
}

My assumption is that reading volatile is cheaper(basically it will be cached most of the time) and locks require additional instructions to be executed


Solution

  • Locking is useful for a few things:

    1. Keeping operations atomic. E.g., an increment operation is technically composed of read, update, write. If another thread can access your field between read and write, you have a race condition that can result in data corruption.

    2. Ensuring updates are visible. When you modify a field, there's no guarantee another thread will see those changes unless it synchronizes with yours.

    3. Safe publication. When you assign an object to a field, unless the object is immutable, it may be seen by another thread in a partially-constructed state. As with the visibility concern, this can be prevented by synchronization.

    In your scenario, none of these concerns are pertinent. The write operation (setInstances()) only updates a reference field, which is inherently an atomic operation. Visibility and safe publication are ensured by making the field volatile, which guarantees that writes are immediately and fully visible to subsequent reads.

    So there's no need to lock at all. Just stick with volatile.