A
Adam Warner
Hi all,
I have a class with this field:
private volatile int[] typeSpecificPos=Const.intArray0;
where Const.intArray0 is simply a new int[0] singleton. As the array grows
empty slots are filled with -1. An element remains constant once it is
filled with a non-negative value (and this is the path that must be fast).
The field is volatile so any thread is guaranteed to always reference the
latest adjusted array assigned to typeSpecificPos.
Is the non-synchronized method below completely thread safe? I'll paste it
in and then explain each step of my approach:
public int getTypeSpecificSlotPos(int type) {
int typedPos;
try {typedPos=typeSpecificPos[type];} catch (ArrayIndexOutOfBoundsException e1) {
synchronized (this) {
try {typedPos=typeSpecificPos[type];} catch (ArrayIndexOutOfBoundsException e2) {
typedPos=TypeEqOffsetMap.getOrCreateOffset(type, this);
int oldLen=typeSpecificPos.length, newLen=Misc.ndxBelowPow2Lim(type);
int[] newArray=new int[newLen];
System.arraycopy(typeSpecificPos, 0, newArray, 0, oldLen);
for (int i=oldLen; i<newLen; ++i) {newArray=-1;}
newArray[type]=typedPos;
typeSpecificPos=newArray;
return typedPos;
}
if (typedPos>=0) return typedPos;
typedPos=TypeEqOffsetMap.getOrCreateOffset(type, this);
typeSpecificPos[type]=typedPos;
return typedPos;
}
}
if (typedPos>=0) return typedPos;
synchronized (this) {
typedPos=typeSpecificPos[type];
if (typedPos>=0) return typedPos;
typedPos=TypeEqOffsetMap.getOrCreateOffset(type, this);
typeSpecificPos[type]=typedPos;
return typedPos;
}
}
First I try to extract an int from the array. I either obtain the int or
the array turns out to be too small. If the array is of sufficient size
then I check whether the element is >=0. If it is I return the value. This
is the fast unsynchronized path.
If the element is negative then I synchronize upon the object containing
the field. Once synchronized I again check whether another thread has
already set the element. If it has I return the non-negative value.
Only one thread succeeds in setting the non-negative value of the element.
If the array was originally too small then I synchronize upon the object
containing the field and again try to read the element. If the element
still cannot be read I grow the array, set the element and assign the new
array to typeSpecificPos before returning the non-negative element.
Upon the second check if the array was of sufficient size then the array
must have been resized in another thread. I check whether the element is
non-negative for safety because I'm unsure whether a different thread with
a different offset that is no longer out of bounds could end up within
this path.
I hope I haven't overlooked anything that could render this approach
non-thread safe!
Regards,
Adam
I have a class with this field:
private volatile int[] typeSpecificPos=Const.intArray0;
where Const.intArray0 is simply a new int[0] singleton. As the array grows
empty slots are filled with -1. An element remains constant once it is
filled with a non-negative value (and this is the path that must be fast).
The field is volatile so any thread is guaranteed to always reference the
latest adjusted array assigned to typeSpecificPos.
Is the non-synchronized method below completely thread safe? I'll paste it
in and then explain each step of my approach:
public int getTypeSpecificSlotPos(int type) {
int typedPos;
try {typedPos=typeSpecificPos[type];} catch (ArrayIndexOutOfBoundsException e1) {
synchronized (this) {
try {typedPos=typeSpecificPos[type];} catch (ArrayIndexOutOfBoundsException e2) {
typedPos=TypeEqOffsetMap.getOrCreateOffset(type, this);
int oldLen=typeSpecificPos.length, newLen=Misc.ndxBelowPow2Lim(type);
int[] newArray=new int[newLen];
System.arraycopy(typeSpecificPos, 0, newArray, 0, oldLen);
for (int i=oldLen; i<newLen; ++i) {newArray=-1;}
newArray[type]=typedPos;
typeSpecificPos=newArray;
return typedPos;
}
if (typedPos>=0) return typedPos;
typedPos=TypeEqOffsetMap.getOrCreateOffset(type, this);
typeSpecificPos[type]=typedPos;
return typedPos;
}
}
if (typedPos>=0) return typedPos;
synchronized (this) {
typedPos=typeSpecificPos[type];
if (typedPos>=0) return typedPos;
typedPos=TypeEqOffsetMap.getOrCreateOffset(type, this);
typeSpecificPos[type]=typedPos;
return typedPos;
}
}
First I try to extract an int from the array. I either obtain the int or
the array turns out to be too small. If the array is of sufficient size
then I check whether the element is >=0. If it is I return the value. This
is the fast unsynchronized path.
If the element is negative then I synchronize upon the object containing
the field. Once synchronized I again check whether another thread has
already set the element. If it has I return the non-negative value.
Only one thread succeeds in setting the non-negative value of the element.
If the array was originally too small then I synchronize upon the object
containing the field and again try to read the element. If the element
still cannot be read I grow the array, set the element and assign the new
array to typeSpecificPos before returning the non-negative element.
Upon the second check if the array was of sufficient size then the array
must have been resized in another thread. I check whether the element is
non-negative for safety because I'm unsure whether a different thread with
a different offset that is no longer out of bounds could end up within
this path.
I hope I haven't overlooked anything that could render this approach
non-thread safe!
Regards,
Adam