diff --git a/spectator-api/src/main/java/com/netflix/spectator/impl/AtomicDouble.java b/spectator-api/src/main/java/com/netflix/spectator/impl/AtomicDouble.java index 99dbb9473..82461c459 100644 --- a/spectator-api/src/main/java/com/netflix/spectator/impl/AtomicDouble.java +++ b/spectator-api/src/main/java/com/netflix/spectator/impl/AtomicDouble.java @@ -15,7 +15,7 @@ */ package com.netflix.spectator.impl; -import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicLongFieldUpdater; /** * Wrapper around AtomicLong to make working with double values easier. @@ -26,22 +26,25 @@ @SuppressWarnings("PMD.MissingSerialVersionUID") public class AtomicDouble extends Number { - private final AtomicLong value; + private volatile long value; + + private static final AtomicLongFieldUpdater VALUE_UPDATER = AtomicLongFieldUpdater.newUpdater( + AtomicDouble.class, "value"); /** Create an instance with an initial value of 0. */ public AtomicDouble() { - this(0.0); + super(); } /** Create an instance with an initial value of {@code init}. */ public AtomicDouble(double init) { super(); - value = new AtomicLong(Double.doubleToLongBits(init)); + this.value = Double.doubleToLongBits(init); } /** Return the current value. */ public double get() { - return Double.longBitsToDouble(value.get()); + return Double.longBitsToDouble(value); } /** Add {@code amount} to the value and return the new value. */ @@ -51,11 +54,11 @@ public double addAndGet(double amount) { double n; long next; do { - v = value.get(); + v = value; d = Double.longBitsToDouble(v); n = d + amount; next = Double.doubleToLongBits(n); - } while (!value.compareAndSet(v, next)); + } while (!VALUE_UPDATER.compareAndSet(this, v, next)); return n; } @@ -66,17 +69,17 @@ public double getAndAdd(double amount) { double n; long next; do { - v = value.get(); - d = Double.longBitsToDouble(v); + v = value; + d = Double.longBitsToDouble(value); n = d + amount; next = Double.doubleToLongBits(n); - } while (!value.compareAndSet(v, next)); + } while (!VALUE_UPDATER.compareAndSet(this, v, next)); return d; } /** Set the value to {@code amount} and return the previous value. */ public double getAndSet(double amount) { - long v = value.getAndSet(Double.doubleToLongBits(amount)); + long v = VALUE_UPDATER.getAndSet(this, Double.doubleToLongBits(amount)); return Double.longBitsToDouble(v); } @@ -87,15 +90,15 @@ public double getAndSet(double amount) { public boolean compareAndSet(double expect, double update) { long e = Double.doubleToLongBits(expect); long u = Double.doubleToLongBits(update); - return value.compareAndSet(e, u); + return VALUE_UPDATER.compareAndSet(this, e, u); } /** Set the current value to {@code amount}. */ public void set(double amount) { - value.set(Double.doubleToLongBits(amount)); + VALUE_UPDATER.set(this, Double.doubleToLongBits(amount)); } - private boolean isGreaterThan(double v1, double v2) { + private static boolean isGreaterThan(double v1, double v2) { return v1 > v2 || Double.isNaN(v2); } @@ -124,4 +127,9 @@ public void max(double v) { @Override public double doubleValue() { return get(); } + + @Override + public String toString() { + return Double.toString(get()); + } } diff --git a/spectator-api/src/main/java/com/netflix/spectator/impl/StepDouble.java b/spectator-api/src/main/java/com/netflix/spectator/impl/StepDouble.java index f7ec5480d..cc59857e7 100644 --- a/spectator-api/src/main/java/com/netflix/spectator/impl/StepDouble.java +++ b/spectator-api/src/main/java/com/netflix/spectator/impl/StepDouble.java @@ -17,7 +17,7 @@ import com.netflix.spectator.api.Clock; -import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicLongFieldUpdater; /** * Utility class for managing a set of AtomicLong instances mapped to a particular step interval. @@ -37,7 +37,10 @@ public class StepDouble implements StepValue { private volatile double previous; private final AtomicDouble current; - private final AtomicLong lastInitPos; + private volatile long lastInitPos; + + private static final AtomicLongFieldUpdater LAST_INIT_POS_UPDATER = AtomicLongFieldUpdater.newUpdater( + StepDouble.class, "lastInitPos"); /** Create a new instance. */ public StepDouble(double init, Clock clock, long step) { @@ -46,13 +49,13 @@ public StepDouble(double init, Clock clock, long step) { this.step = step; previous = init; current = new AtomicDouble(init); - lastInitPos = new AtomicLong(clock.wallTime() / step); + lastInitPos = clock.wallTime() / step; } private void rollCount(long now) { final long stepTime = now / step; - final long lastInit = lastInitPos.get(); - if (lastInit < stepTime && lastInitPos.compareAndSet(lastInit, stepTime)) { + final long lastInit = lastInitPos; + if (lastInit < stepTime && LAST_INIT_POS_UPDATER.compareAndSet(this, lastInit, stepTime)) { final double v = current.getAndSet(init); // Need to check if there was any activity during the previous step interval. If there was // then the init position will move forward by 1, otherwise it will be older. No activity @@ -97,13 +100,13 @@ public double poll(long now) { /** Get the timestamp for the end of the last completed interval. */ @Override public long timestamp() { - return lastInitPos.get() * step; + return lastInitPos * step; } @Override public String toString() { return "StepDouble{init=" + init + ", previous=" + previous + ", current=" + current.get() - + ", lastInitPos=" + lastInitPos.get() + '}'; + + ", lastInitPos=" + lastInitPos + '}'; } } diff --git a/spectator-api/src/main/java/com/netflix/spectator/impl/StepLong.java b/spectator-api/src/main/java/com/netflix/spectator/impl/StepLong.java index 566a7d865..c957121b7 100644 --- a/spectator-api/src/main/java/com/netflix/spectator/impl/StepLong.java +++ b/spectator-api/src/main/java/com/netflix/spectator/impl/StepLong.java @@ -18,6 +18,7 @@ import com.netflix.spectator.api.Clock; import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicLongFieldUpdater; /** * Utility class for managing a set of AtomicLong instances mapped to a particular step interval. @@ -37,7 +38,10 @@ public class StepLong implements StepValue { private volatile long previous; private final AtomicLong current; - private final AtomicLong lastInitPos; + private volatile long lastInitPos; + + private static final AtomicLongFieldUpdater LAST_INIT_POS_UPDATER = AtomicLongFieldUpdater.newUpdater( + StepLong.class, "lastInitPos"); /** Create a new instance. */ public StepLong(long init, Clock clock, long step) { @@ -46,13 +50,13 @@ public StepLong(long init, Clock clock, long step) { this.step = step; previous = init; current = new AtomicLong(init); - lastInitPos = new AtomicLong(clock.wallTime() / step); + lastInitPos = clock.wallTime() / step; } private void rollCount(long now) { final long stepTime = now / step; - final long lastInit = lastInitPos.get(); - if (lastInit < stepTime && lastInitPos.compareAndSet(lastInit, stepTime)) { + final long lastInit = lastInitPos; + if (lastInit < stepTime && LAST_INIT_POS_UPDATER.compareAndSet(this, lastInit, stepTime)) { final long v = current.getAndSet(init); // Need to check if there was any activity during the previous step interval. If there was // then the init position will move forward by 1, otherwise it will be older. No activity @@ -97,13 +101,13 @@ public long poll(long now) { /** Get the timestamp for the end of the last completed interval. */ @Override public long timestamp() { - return lastInitPos.get() * step; + return lastInitPos * step; } @Override public String toString() { return "StepLong{init=" + init + ", previous=" + previous + ", current=" + current.get() - + ", lastInitPos=" + lastInitPos.get() + '}'; + + ", lastInitPos=" + lastInitPos + '}'; } } diff --git a/spectator-api/src/test/java/com/netflix/spectator/impl/AtomicDoubleTest.java b/spectator-api/src/test/java/com/netflix/spectator/impl/AtomicDoubleTest.java index 0a1a865bf..09b36b0f3 100644 --- a/spectator-api/src/test/java/com/netflix/spectator/impl/AtomicDoubleTest.java +++ b/spectator-api/src/test/java/com/netflix/spectator/impl/AtomicDoubleTest.java @@ -122,4 +122,12 @@ public void maxValueInfinity() { v.max(Double.POSITIVE_INFINITY); Assertions.assertEquals(0.0, v.get(), 1e-12); } + + @Test + public void testToString() { + AtomicDouble v = new AtomicDouble(0.0); + Assertions.assertEquals("0.0", v.toString()); + v.set(-100.5); + Assertions.assertEquals("-100.5", v.toString()); + } }