I've written a Java class and someone has reviewed the code and insisted that there could be a race condition in method calculate. Here's a simplified version of the class code:
public class MyClass {
private List<Integer> list;
private final ReadWriteLock lock;
public MyClass() {
list = new ArrayList<>();
lock = new ReentrantReadWriteLock();
}
public void add(Integer integer) {
lock.writeLock().lock();
try {
list.add(integer);
} finally {
lock.writeLock().unlock();
}
}
public void deleteAll() {
lock.writeLock().lock();
try {
list.clear();
} finally {
lock.writeLock().unlock();
}
}
public Integer calculate() {
List<Integer> newList = new ArrayList<>();
Integer result = 0;
lock.readLock().lock();
try {
list.forEach(integer -> {
// calculation logic that reads values from 'list' and adds only a subset of elements from 'list' in 'newList'
});
} finally {
lock.readLock().unlock();
}
setList(newList);
return result;
}
private void setList(List<Integer> newList) {
lock.writeLock().lock();
try {
list = newList;
} finally {
lock.writeLock().unlock();
}
}
}
Now my question is:
Can a race condition really happen in this method, and if so how can I solve it (either using locks or using any other method to make the class thread safe)?
Any advice would be appreciated.
There is a time gap between creation of newList and call to setList(newList). We may assume this time gap is arbitrary long, and everything can happen when it lasts, e.g. another thread adds an object which must be retained, but it will be lost when call to setList(newList) removes list with that new object.
In fact, the method calculate is modifying and should do all the work under write lock.
To clarify the above ... the statement
List<Integer> newList = new ArrayList<>();
... instantiates a data-structure (list ...) that will subsequently be used within the block of code that is intended to be protected by lock.readLock().lock();, but is not contained within it. Therefore it is not protected.
To remedy the problem, the declaration of newList should not include initialization. Nothing which affects the presumed value of this variable should exist outside of the lock-protected block.
Related
Within ConcurrentHashMap.compute() I increment and decrement some long value located in shared memory. Read, increment/decrement only gets performed within compute method on the same key.
So the access to long value is synchronised by locking on ConcurrentHashMap segment, thus increment/decrement is atomic. My question is: Does this synchronisation on a map guarantee visibility for long value? Can I rely on Map's internal synchronisation or should I make my long value volatile?
I know that when you explicitly synchronise on a lock, visibility is guaranteed. But I do not have perfect understanding of ConcurrentHashMap internals. Or maybe I can trust it today but tomorrow ConcurrentHashMap's internals may somehow change: exclusive access will be preserved, but visibility will disappear... and it is an argument to make my long value volatile.
Below I will post a simplified example. According to the test there is no race condition today. But can I trust this code long-term without volatile for long value?
class LongHolder {
private final ConcurrentMap<Object, Object> syncMap = new ConcurrentHashMap<>();
private long value = 0;
public void increment() {
syncMap.compute("1", (k, v) -> {
if (++value == 2000000) {
System.out.println("Expected final state. If this gets printed, this simple test did not detect visibility problem");
}
return null;
});
}
}
class IncrementRunnable implements Runnable {
private final LongHolder longHolder;
IncrementRunnable(LongHolder longHolder) {
this.longHolder = longHolder;
}
#Override
public void run() {
for (int i = 0; i < 1000000; i++) {
longHolder.increment();
}
}
}
public class ConcurrentMapExample {
public static void main(String[] args) throws InterruptedException {
LongHolder longholder = new LongHolder();
Thread t1 = new Thread(new IncrementRunnable(longholder));
Thread t2 = new Thread(new IncrementRunnable(longholder));
t1.start();
t2.start();
}
}
UPD: adding another example which is closer to the code I am working on. I would like to remove map entries when no one else is using the object. Please note that reading and writing of the long value happens only inside of remapping function of ConcurrentHashMap.compute:
public class ObjectProvider {
private final ConcurrentMap<Long, CountingObject> map = new ConcurrentHashMap<>();
public CountingObject takeObjectForId(Long id) {
return map.compute(id, (k, v) -> {
CountingObject returnLock;
returnLock = v == null ? new CountingObject() : v;
returnLock.incrementUsages();
return returnLock;
});
}
public void releaseObjectForId(Long id, CountingObject o) {
map.compute(id, (k, v) -> o.decrementUsages() == 0 ? null : o);
}
}
class CountingObject {
private int usages;
public void incrementUsages() {
--usages;
}
public int decrementUsages() {
return --usages;
}
}
UPD2: I admit that I failed to provide the simplest code examples previously, posting a real code now:
public class LockerUtility<T> {
private final ConcurrentMap<T, CountingLock> locks = new ConcurrentHashMap<>();
public void executeLocked(T entityId, Runnable synchronizedCode) {
CountingLock lock = synchronizedTakeEntityLock(entityId);
try {
lock.lock();
try {
synchronizedCode.run();
} finally {
lock.unlock();
}
} finally {
synchronizedReturnEntityLock(entityId, lock);
}
}
private CountingLock synchronizedTakeEntityLock(T id) {
return locks.compute(id, (k, l) -> {
CountingLock returnLock;
returnLock = l == null ? new CountingLock() : l;
returnLock.takeForUsage();
return returnLock;
});
}
private void synchronizedReturnEntityLock(T lockId, CountingLock lock) {
locks.compute(lockId, (i, v) -> lock.returnBack() == 0 ? null : lock);
}
private static class CountingLock extends ReentrantLock {
private volatile long usages = 0;
public void takeForUsage() {
usages++;
}
public long returnBack() {
return --usages;
}
}
}
No, this approach will not work, not even with volatile. You would have to use AtomicLong, LongAdder, or the like, to make this properly thread-safe. ConcurrentHashMap doesn't even work with segmented locks these days.
Also, your test does not prove anything. Concurrency issues by definition don't happen every time. Not even every millionth time.
You must use a proper concurrent Long accumulator like AtomicLong or LongAdder.
Do not get fooled by the line in the documentation of compute:
The entire method invocation is performed atomically
This does work for side-effects, like you have in that value++; it only works for the internal data of ConcurrentHashMap.
The first thing that you miss is that locking in CHM, the implementation has changed a lot (as the other answer has noted). But even if it did not, your understanding of the:
I know that when you explicitly synchronize on a lock, visibility is guaranteed
is flawed. JLS says that this is guaranteed when both the reader and the writer use the same lock; which in your case obviously does not happen; as such no guarantees are in place. In general happens-before guarantees (that you would require here) only work for pairs, for both reader and writer.
Can I synchronize method by parameter?
For example - I get person to some method and I want to do some operation for person, but if few thread call this method for the same person I want to do it one by one.
private void dosomething(Long id, Person person) {
dosomethingelse(id, person);
}
How to call dosomethingelse (id, person) only for the same id one by one? but I want that this code for different id-s can be called multithreadly
I wrote this code, but maybe something wrong here or something can be better.
public static class LatchByValue <T> {
public void latch(T value, ConsumerWithException<T> consummer) throws Exception {
CountDownLatch latch = new CountDownLatch(1);
try {
CountDownLatch previousLatch = null;
// we are checking if another thread is already calling this method with the same id
// if sync has CountDownLatch so another thread is already calling this method
// or we put our latch and go on
while ((previousLatch = sync.putIfAbsent(value, latch)) != null) {
try {
// we are waiting for another thread, we are waiting for all threads that put their latch before our thread
previousLatch.await();
} catch (InterruptedException e) {
return;
}
}
consummer.accept(value);
} finally {
latch.countDown();
sync.remove(value, latch);
}
}
private ConcurrentHashMap<T, CountDownLatch> sync = new ConcurrentHashMap<>();
}
Example:
LatchByValue<Long> latch = new LatchByValue<>();
private void dosomething(Long id, Person person) {
latch.latch(
id,
currentId -> { dosomethingelse(currentId, person); }
);
}
Problem with using a CountdownLatch is that you can't "increment" the count so you need to replace the existing latch when it's been used, which complicates the code.
You could instead use a Semaphore with one permit which would allow you to do the same thing but in a simpler way.
Semaphore s = sync.computeIfAbsent(value, x -> new Semaphore(1, true));
s.acquire(); //this blocks and throws InterruptedException, which you need to handle
try {
consummer.accept(value);
} finally {
s.release();
}
You can use synchronized keyword on the parameter passed (culprit: it cannot be null!). And that also allows you to stop worrying about re-acquiring the lock (it's reentrant).
So the implementation would look like:
private void doSomething(Long id, Person person) {
synchronized (person) {
// do something
}
}
Remember that any other accesses (not in doSomething call) also would need to have the synchronization block, e.g.:
// another method, unrelated, but does something with 'person'
private void doSomethingElse(Person person, ... /* other arguments */) {
synchronized (person) {
// do something
}
}
It would be good document (in Person's javadoc) that the user needs to acquire the lock for that object.
If you want to provide a critical section for <id, person> tuple, you'd need to change your API a bit - and then pass that object around in your application.
private void doSomething(IdAndPerson idAndPerson) {
synchronized (idAndPerson) {
// do something
}
}
class IdAndPerson {
private final Long id;
private final Person person;
// constructor etc.
}
private static final Set<Long> lockedIds = new HashSet<>();
private void lock(Long id) throws InterruptedException {
synchronized (lockedIds) {
while (!lockedIds.add(id)) {
lockedIds.wait();
}
}
}
private void unlock(Long id) {
synchronized (lockedIds) {
lockedIds.remove(id);
lockedIds.notifyAll();
}
}
public void doSomething(Long id) throws InterruptedException {
try {
lock(id);
//Put your code here.
//For different ids it is executed in parallel.
//For equal ids it is executed synchronously.
} finally {
unlock(id);
}
}
id can be not only an 'Long' but any class with correctly overridden 'equals' and 'hashCode' methods.
try-finally - is very important - you must guarantee to unlock waiting threads after your operation even if your operation threw exception.
It will not work if your back-end is distributed across multiple servers/JVMs.
I have a problem in concurrency programming in java. Please look at the code below. The result which system should print to me, changes every time I run the program. Although I’ve synchronized the operation of adding value to sub variable, but the result changes every time. I think I’ve made a mistake somewhere. But I do not know where.
public class Test {
public static void main(String[] args) {
final MyClass mClass = new MyClass();
int size = 10;
final CountDownLatch cdl = new CountDownLatch(size);
for(int i = 0; i < size; i++){
Thread t = new Thread(new Runnable() {
#Override
public void run() {
for(int number = 0; number < 100000; number++){
mClass.addToSub(number);
}
cdl.countDown();
}
});
t.start();
}
try {
cdl.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
//the result changes every time!!!!!!!!
System.out.println(mClass.getSub());
}
public static class MyClass {
private Long sub = 0L;
public long getSub() {
synchronized (sub) {
return sub;
}
}
public void addToSub(long value){
synchronized (sub) {
sub += value;
}
}
}
}
What you are getting wrong here is not the multi-threading. What is causing this issue is a java feature called auto-boxing.
Your variable sub has the type Long which is a reference to an object (Long and long are different).
You need to have an object to synchronize on in java so you can not use just a normal long.
The problem here is that a Long is immutable meaning the value does not change. So when you do sub += value you are actually doing sub = Long.valueOf(sub.longValue() + value) witch is creating a new object.
So the current thread only has the previous object locked so new threads can still change the reference sub.
What you want to do is synchronize on a reference that wont change, i.e this
public void addToSub(long value){
synchronized (this) {
sub += value;
}
}
Or more terse:
public synchronized void addToSub(long value) {
sub += value;
}
And you should probably use long and not Long.
EDIT
As noted in Thomas Timbuls answer you probably want to use AtomicLong as that gives you thread-safety by default and potentially much better performance (as the threads don't need to wait for each-other).
In addToSub you are changing the value on which you synchronize. Effectively this means that there is no synchronization at all.
Either sync on this, or even better, use AtomicLong and avoid both your problem as well as synchronization overhead (Thread contention):
public static class MyClass {
private AtomicLong sub = new AtomicLong();
public long getSub() {
return sub.get();
}
public void addToSub(long value){
sub.addAndGet(value);
}
}
The Atomic* classes are specifically designed for this type of usecase, where a single variable is updated by multiple Threads, and where synchronize could result in heavy thread contention. If you are dealing with Collections, look towards the ones in java.util.concurrent.*
Edit:
Thanks for the correction of addAndGet vs incrementAndGet.
You're synchronizing on a non-final value:
synchronized (sub) {
This means that as soon as you change it to some other value:
sub += value;
anything which isn't already waiting at the synchronized block can proceed, because nothing is holding the monitor for this new value.
Synchronize on this instead (or some other unchanging value):
synchronized (this) {
sub is an object (Long), change to long and add a private Object for the synchronized. Then it will work.
public static class MyClass {
private Object locker = new Object();
private long sub = 0L;
public long getSub() {
synchronized (locker) {
return sub;
}
}
public void addToSub(long value){
synchronized (locker) {
sub += value;
}
}
}
I have the following class:
public class MyClass{
private List<Integer> ints = new LinkedList<Integer>();
public List<Integer> getInts(){
return ints;
}
public synchronized void doAction(){
//Do some with the list
}
}
I need to allow only one thread at the time having acces to the List. I would do that as follows:
public class MyClass{
private List<Integer> ints = new LinkedList<Integer>();
private static final Semaphore s = new Semaphore(1);
public List<Integer> getInts(){
s.acquire();
return ints;
}
public void release(){
s.release();
}
public synchronized void doAction(){
s.acquire();
//Do some with the list
s.release();
}
}
But the implementaion is obviously not reliable, because if the client request the List through the getter for adding some elements into it and forget to call release() we'll get into troubles if try to invoke the doAction method.
What is the solution for the problem?
Don't allow the client to get the reference. Put all the methods that work on the list to MyClass and synchronize them.
You can allow the users to get a snapshot copy of the list however.
You could use a synchronized list:
private List<Integer> ints = Collections.synchronizedList(new LinkedList<Integer>());
I am making a particle emitter.
Every "Rendered" object is stored in a HashSet, and when there's lots of particles on the screen, the console spits out concurrent modification exceptions. I usually have a short lifetime on these particles so they get deleted after several seconds, but I am sure this could potentially be a problem in the future. How can I fix this?
EDIT: Code:
public class UpdatedManager {
private static Set<Updated> updates = new HashSet<>();
private UpdatedManager() {}
public static Set<Updated> getUpdates() {
return new HashSet<Updated>(updates);
}
public static boolean registerUpdated(Updated u) {
return updates.add(u);
}
public static boolean unregisterUpdated(Updated u) {
return updates.remove(u);
}
public static void update() {
for (Updated up : new HashSet<Updated>(updates))
up.update();
}
public static Set<GameObject> getGameObjects() {
Set<GameObject> objs = new HashSet<>();
for (Updated up : new HashSet<Updated>(updates)) {
if (up instanceof GameObject)
objs.add((GameObject) up);
}
return objs;
}
public static Set<GameObject> getGameObjectsByName(String name) {
Set<GameObject> objs = new HashSet<>();
for (GameObject go : new HashSet<GameObject>(getGameObjects())) {
if (go.getName() != null && go.getName().equals(name))
objs.add(go);
}
return objs;
}
public static Set<Particle> getParticles() {
Set<Particle> parts = new HashSet<>();
for (Updated up : new HashSet<Updated>(updates)) {
if (up instanceof Particle)
parts.add((Particle) up);
}
return parts;
}
}
A ConcurrentModificationException means you modified the set while iterating over it. It does not mean the set is full.
For example, the following code will throw a ConcurrentModificationException:
Set<String> set = new HashSet<>();
set.add("Hello");
for(String s : set)
set.add(s+" world");
Note that you are not guaranteed to get a ConcurrentModificationException, so you should avoid catching it. You should instead fix your code so that it doesn't cause the problem.
What makes you think that the set is full?
Concurrent modification exceptions mean that the set is being accessed by different threads in an unsafe manner.
Try a synchronised set using the Collections utilities
HashSet hashSet = new HashSet();
Set set = Collections.synchronizedSet(hashSet);
or use the synchronized keyword for the method accessing the set.