JavaのAtomicパッケージにはAtomicFloatとかAtomicDoubleが無くて,ドキュメントを見ると
また、Float.floatToIntBits や Float.intBitstoFloat 変換を使用して float を保持したり、Double.doubleToLongBits や Double.longBitsToDouble 変換を使用して double を保持したりできます。
ということなので,作ってみた.
AtomicFloat.java
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicFloat {
private static final int toI(float f){
return Float.floatToRawIntBits(f);
}
private static final float toF(int i){
return Float.intBitsToFloat(i);
}
private final AtomicInteger ai;
public AtomicFloat(){
ai = new AtomicInteger();
}
public AtomicFloat(float f){
ai = new AtomicInteger(toI(f));
}
public float get() {
return toF(ai.get());
}
public float getAndAdd(float delta) {
int i;
do {
i = ai.get();
} while(!ai.compareAndSet(i, toI(toF(i) + delta)));
return toF(i);
}
public float addAndGet(float delta){
int i;
do {
i = ai.get();
} while(!ai.compareAndSet(i, toI(toF(i) + delta)));
return get();
}
public boolean compareAndSet(float expect, float update) {
return ai.compareAndSet(toI(expect), toI(update));
}
public void set(float newValue) {
ai.set(toI(newValue));
}
public float getAndSet(float newValue) {
return toF(ai.getAndSet(toI(newValue)));
}
public boolean weakCompareAndSet(float expect, float update) {
return ai.weakCompareAndSet(toI(expect), toI(update));
}
public void lazySet(float newValue) {
ai.lazySet(toI(newValue));
}
}
AtomicLong.java
import java.util.concurrent.atomic.AtomicLong;
public class AtomicDouble {
private static final long toL(double d){
return Double.doubleToRawLongBits(d);
}
private static final double toD(long l){
return Double.longBitsToDouble(l);
}
private final AtomicLong al;
public AtomicDouble(){
al = new AtomicLong();
}
public AtomicDouble(double d){
al = new AtomicLong(toL(d));
}
public double get() {
return toD(al.get());
}
public double getAndAdd(double delta) {
long l;
do {
l = al.get();
} while(!al.compareAndSet(l, toL(toD(l) + delta)));
return toD(l);
}
public double addAndGet(double delta){
long l;
do {
l = al.get();
} while(!al.compareAndSet(l, toL(toD(l) + delta)));
return get();
}
public boolean compareAndSet(double expect, double update) {
return al.compareAndSet(toL(expect), toL(update));
}
public void set(double newValue) {
al.set(toL(newValue));
}
public double getAndSet(double newValue) {
return toD(al.getAndSet(toL(newValue)));
}
public boolean weakCompareAndSet(double expect, double update) {
return al.weakCompareAndSet(toL(expect), toL(update));
}
public void lazySet(double newValue) {
al.lazySet(toL(newValue));
}
}
AtomicFloat,AtomicDoubleの両方ともほぼ同じで,ドキュメントにある通りdoubleToRawLongBits
とかlongBitsToDouble
とかを使ってintやlongに変換してラップしてるだけです.
唯一,加算する場所はノンブロッキング的な書き方をしないといけないのでちゃんと実装しています.
で,これと同じ方法で計測してみたところ
AtomicFloatの結果
Sync : 12816.4416
Atomic : 8969.0106
AtomicDoubleの結果
Sync : 13165.4701
Atomic : 9005.6199
ということで両方ともsynchronizedするより速くなっていました.