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?
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;
}
}
}
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
Locking is useful for a few things:
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.
Ensuring updates are visible. When you modify a field, there's no guarantee another thread will see those changes unless it synchronizes with yours.
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
.