He encontrado AtomicInteger
, AtomicLong
, pero donde AtomicFloat
(o AtomicDouble
)? Tal vez hay algún truco?Java: ¿no hay AtomicFloat o AtomicDouble?
Respuesta
Los documentos de la API para los java.util.concurrent
package indica lo siguiente:
[...] Además, se proporcionan clases sólo para aquellos tipos que son generalmente útiles en aplicaciones previstas. Por ejemplo, no existe una clase atómica para representar byte. En los casos poco frecuentes en los que le gustaría hacerlo, puede usar un
AtomicInteger
para mantener los valores de bytes y el moldear de manera apropiada. También puede mantener flotantes conFloat.floatToIntBits
yFloat.intBitstoFloat
conversiones, y se duplica con las conversionesDouble.doubleToLongBits
yDouble.longBitsToDouble
.
No estoy afirmando que es una solución cómoda, pero que parece ser la explicación. Supongo que es probable que desee para envolver un AtomicInteger
y proporcionar métodos de acceso para getFloat
/setFloat
etc.
realidad tiene alrededor de escribir uno. Aquí tienes:
import java.util.concurrent.atomic.AtomicInteger;
import static java.lang.Float.*;
class AtomicFloat extends Number {
private AtomicInteger bits;
public AtomicFloat() {
this(0f);
}
public AtomicFloat(float initialValue) {
bits = new AtomicInteger(floatToIntBits(initialValue));
}
public final boolean compareAndSet(float expect, float update) {
return bits.compareAndSet(floatToIntBits(expect),
floatToIntBits(update));
}
public final void set(float newValue) {
bits.set(floatToIntBits(newValue));
}
public final float get() {
return intBitsToFloat(bits.get());
}
public float floatValue() {
return get();
}
public final float getAndSet(float newValue) {
return intBitsToFloat(bits.getAndSet(floatToIntBits(newValue)));
}
public final boolean weakCompareAndSet(float expect, float update) {
return bits.weakCompareAndSet(floatToIntBits(expect),
floatToIntBits(update));
}
public double doubleValue() { return (double) floatValue(); }
public int intValue() { return (int) get(); }
public long longValue() { return (long) get(); }
}
También podría usar AtomicDouble en Guava http://docs.guava-libraries.googlecode.com/git-history/v11.0.2/javadoc/com/google/common/util/concurrent/AtomicDouble.html – codeplay
Esto le falta a la característica que sería útil: 'addAndGet' (o' getAndAdd'; no importa cuál). Guava 'AtomicDouble' y Java 8' DoubleAdder' lo tienen. Todas estas preguntas sobre el caso de uso: ¡acumular una suma de residuos provenientes de diferentes hilos, naturalmente! –
@JimPivarski, 'addAndGet' se puede implementar de la misma manera que se implementa' getAndSet'. Simplemente vaya a través de los bits del respaldo AtomicInteger. – aioobe
En su lugar, podrías usar un AtomicReference<Float>
. Creo que AtomicInteger
y AtomicLong
obtienen clases especiales porque son útiles para contar.
'AtomicReference.compareAndSet' se compara por identidad y no por igualdad, por lo que no sustituye al hipotético' AtomicFloat'. –
¿Estás seguro de que lo necesitas?
Las clases atómicas se diseñan principalmente como elementos básicos para implementar estructuras de datos no bloqueantes y clases de infraestructura relacionadas. El método compareAndSet no es un reemplazo general para el bloqueo. Se aplica solo cuando las actualizaciones críticas para un objeto están confinadas a una única variable.
Here es una explicación de los problemas que las variables atómicas fueron diseñadas para resolver.
* ¿Estás seguro de que lo necesitas? * - Tal vez solo tiene curiosidad :-) Creo que es una pregunta perfectamente legítima. – aioobe
@aioobe Sí, pero creo que es mejor leer sobre por qué 'AtomicInteger' existe que proporcionar una solución que probablemente no sea realmente necesaria. –
Sería terriblemente ineficiente de implementar (pero sería posible). Por sí mismo no tiene sentido hablar de tipos de datos atómicos, porque las operaciones con tipos de datos son atómicas, no los tipos de datos en sí (tal vez usted lo sepa, pero solo quiere aclarar este punto). Con todo este objeto se confunde. Los necesita con mucha frecuencia en el sistema operativo para administrar bloqueos y semáforos, por eso muchos procesadores tienen instrucciones enteras atómicas. Para los flotadores, generalmente no se implementan, por lo que se implementan, envolviendo la operación de flotación en un bloque protegido por un semáforo (que se implementa con operaciones atómicas).
En java de alto nivel no hay problema para hacer este bloqueo para flotadores usted mismo (y tiene razón, podrían haberlo implementado), pero para la eficiencia debe implementarlos con el bajo nivel de asm, por lo que es muy práctico si proporcionar a los java de alto nivel alguna función que utilice las instrucciones de bajo nivel de asm.
En realidad, vi muy pocas aplicaciones donde las operaciones de flotación atómica son útiles. Me encontré con ellos, pero muy raro y siempre fue posible reformular el problema de que la concurrencia no sucedió en la parte flotante.
También estoy sorprendido de que no haya una solución integrada. El caso de uso consiste en obtener la suma de valores en coma flotante emitida por una colección de subprocesos simultáneos sin que la memoria utilice una escala con el número de valores. Por ejemplo, los subprocesos simultáneos son motores de predicción y desea supervisar la suma de residuos de verdad menos predicha de todos los motores de predicción en un solo lugar. Los intentos simultáneos de agregar a un contador ingenuo daría como resultado recuentos perdidos (exactamente de la misma manera que los contadores de enteros).
Un ConcurrentLinkedQueue
puede recoger los valores de resumir, pero a menos que haya un hilo dedicado a reducir esa cola (en constante funcionamiento result += q.poll()
hasta sondeo vuelve null
, entonces q.add(result)
y esperar un momento a que se llene de nuevo), el tamaño de la la cola crecerá a la cantidad de valores a sumar.
Java 8 tiene DoubleAdder
y guayaba tiene AtomicDouble
(véanse los comentarios sobre otras cuestiones), pero eso no quiere ayudar a los desarrolladores de bibliotecas de orientación edad Java con dependencias mínimas. Miré una muestra de DoubleAdder code y AtomicDouble code, y lo que encontré me sorprendió: simplemente vuelven a intentar la suma seguida de compareAndSet
hasta que no sea erróneo. El número de subprocesos que intentan escribir puede aumentar mientras hay contienda, pero a menos que estén en un paso de bloqueo perfecto, algunos ganarán la carrera y se apartarán mientras otros continúan reintentando.
Aquí hay una aplicación Scala de lo que hacen:
class AtomicDouble {
private val value = new AtomicReference(java.lang.Double.valueOf(0.0))
@tailrec
final def getAndAdd(delta: Double): Double = {
val currentValue = value.get
val newValue = java.lang.Double.valueOf(currentValue.doubleValue + delta)
if (value.compareAndSet(currentValue, newValue))
currentValue.doubleValue
else
getAndAdd(delta) // try, try again
}
}
y una traducción de Java intentado:
class AtomicDouble {
private AtomicReference<Double> value = new AtomicReference(Double.valueOf(0.0));
double getAndAdd(double delta) {
while (true) {
Double currentValue = value.get();
Double newValue = Double.valueOf(currentValue.doubleValue() + delta);
if (value.compareAndSet(currentValue, newValue))
return currentValue.doubleValue();
}
}
}
Funciona (versión Scala probado con cientos de hilos), y proporciona una manera de generalizar desde Double
.
Sin embargo, no veo ninguna razón por la que esto sea más rápido o preferible a la sincronización en la escritura solamente. Una solución de bloqueo también haría que algunos subprocesos esperen mientras que otros incrementan el contador, pero con la garantía de que todos terminarán finalmente (sin depender de un tiempo imperfecto) y no se desperdiciará la CPU (no calcule la suma hasta que sepa que puede hacerlo). actualizarla). Entonces, ¿por qué hacer esto?
La sincronización es muy costosa. En el tiempo necesario para suspender y reactivar un hilo, podría ejecutar el código en el ciclo while un par de miles de veces. – TomWolk
No es un problema de Java, todos los idiomas sufren de esto.
Las instrucciones de montaje que comparan la atómica y las operaciones de canje de compilación hasta son variantes de: http://x86.renejeschke.de/html/file_module_x86_id_41.html
Todos ellos operan en números enteros y la naturaleza segmentada de la FPU hace que sea mucho más difícil de implementar para los flotadores/dobles.
Aunque algunas de las respuestas aquí algunos aplicación no parecen ofrecer una completa y completar una.
Éste es. Es AtomicDouble y no AtomicFloat ya que tiene mayor precisión que float.
Como algunas de las implementaciones incluidas en este documento, incluyendo la guayaba Google carecen de funciones actualizador, por lo que las operaciones tales como:
average.set(average.get() > x ? dosomething(y) : y) ;
no pueden utilizarse plenamente atómica.Éste le permite hacer:
average.updateAndGet(new DoubleUnaryOperator() {
@Override
public double applyAsDouble(double previous) {
return previous > x ? dosomething(y) : y;
}
});
aplicación completa a continuación con los mismos métodos que se encuentran en AtomicLong:
import static java.lang.Double.doubleToLongBits;
import static java.lang.Double.longBitsToDouble;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.DoubleBinaryOperator;
import java.util.function.DoubleUnaryOperator;
public final class AtomicDouble extends Number {
private static final long serialVersionUID = 12327722191124184L;
private final AtomicLong bits;
public AtomicDouble() {
this(0.0d);
}
public AtomicDouble(double initialValue) {
bits = new AtomicLong(toLong(initialValue));
}
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(double expect, double update) {
return bits.compareAndSet(toLong(expect), toLong(update));
}
/**
* Sets to the given value.
*
* @param newValue the new value
*/
public final void set(double newValue) {
bits.set(toLong(newValue));
}
public final double get() {
return toDouble(bits.get());
}
/**
* Atomically sets to the given value and returns the old value.
*
* @param newValue the new value
* @return the previous value
*/
public final double getAndSet(double newValue) {
return toDouble(bits.getAndSet(toLong(newValue)));
}
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* <p><a href="package-summary.html#weakCompareAndSet">May fail
* spuriously and does not provide ordering guarantees</a>, so is
* only rarely an appropriate alternative to {@code compareAndSet}.
*
* @param expect the expected value
* @param update the new value
* @return {@code true} if successful
*/
public final boolean weakCompareAndSet(double expect, double update) {
return bits.weakCompareAndSet(toLong(expect), toLong(update));
}
/**
* Atomically updates the current value with the results of
* applying the given function to the current and given values,
* returning the updated value. The function should be
* side-effect-free, since it may be re-applied when attempted
* updates fail due to contention among threads. The function
* is applied with the current value as its first argument,
* and the given update as the second argument.
*
* @param x the update value
* @param accumulatorFunction a side-effect-free function of two arguments
* @return the updated value
* @since 1.8
*/
public final double accumulateAndGet(double x, DoubleBinaryOperator accumulatorFunction) {
double prev, next;
do {
prev = get();
next = accumulatorFunction.applyAsDouble(prev, x);
} while (!compareAndSet(prev, next));
return next;
}
/**
* Atomically adds the given value to the current value.
*
* @param delta the value to add
* @return the updated value
*/
public final double addAndGet(double delta) {
return toDouble(bits.addAndGet(toLong(delta)));
}
/**
* Atomically decrements by one the current value.
*
* @return the updated value
*/
public final double decrementAndGet() {
return addAndGet(-1.0d);
}
/**
* Atomically updates the current value with the results of
* applying the given function to the current and given values,
* returning the previous value. The function should be
* side-effect-free, since it may be re-applied when attempted
* updates fail due to contention among threads. The function
* is applied with the current value as its first argument,
* and the given update as the second argument.
*
* @param x the update value
* @param accumulatorFunction a side-effect-free function of two arguments
* @return the previous value
* @since 1.8
*/
public final double getAndAccumulate(double x, DoubleBinaryOperator accumulatorFunction) {
double prev, next;
do {
prev = get();
next = accumulatorFunction.applyAsDouble(prev, x);
} while (!compareAndSet(prev, next));
return prev;
}
/**
* Atomically adds the given value to the current value.
*
* @param delta the value to add
* @return the previous value
*/
public final double getAndAdd(double delta) {
return toDouble(bits.getAndAdd(toLong(delta)));
}
public final double getAndDecrement() {
return getAndAdd(-1.0d);
}
/**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final double getAndIncrement() {
return getAndAdd(1.0d);
}
/**
* Atomically increments by one the current value.
*
* @return the updated value
*/
public final double incrementAndGet() {
return addAndGet(1.0d);
}
/**
* Atomically updates the current value with the results of
* applying the given function, returning the previous value. The
* function should be side-effect-free, since it may be re-applied
* when attempted updates fail due to contention among threads.
*
* @param updateFunction a side-effect-free function
* @return the previous value
* @since 1.8
*/
public final double getAndUpdate(DoubleUnaryOperator updateFunction) {
double prev, next;
do {
prev = get();
next = updateFunction.applyAsDouble(prev);
} while (!compareAndSet(prev, next));
return prev;
}
/**
* Eventually sets to the given value.
*
* @param newValue the new value
* @since 1.6
*/
public final void lazySet(double newValue) {
bits.lazySet(toLong(newValue));
// unsafe.putOrderedLong(this, valueOffset, newValue);
}
/**
* Returns the value of this {@code AtomicLong} as a {@code long}.
*/
public long longValue() {
return (long) get();
}
/**
* Returns the String representation of the current value.
*
* @return the String representation of the current value
*/
public String toString() {
return Double.toString(get());
}
/**
* Atomically updates the current value with the results of
* applying the given function, returning the updated value. The
* function should be side-effect-free, since it may be re-applied
* when attempted updates fail due to contention among threads.
*
* @param updateFunction a side-effect-free function
* @return the updated value
* @since 1.8
*/
public final double updateAndGet(DoubleUnaryOperator updateFunction) {
double prev, next;
do {
prev = get();
next = updateFunction.applyAsDouble(prev);
} while (!compareAndSet(prev, next));
return next;
}
/**
* Returns the value of this {@code AtomicLong} as an {@code int}
* after a narrowing primitive conversion.
*
* @jls 5.1.3 Narrowing Primitive Conversions
*/
public int intValue() {
return (int) get();
}
/**
* Returns the value of this {@code AtomicLong} as a {@code float}
* after a widening primitive conversion.
*
* @jls 5.1.2 Widening Primitive Conversions
*/
public float floatValue() {
return (float) get();
}
/**
* Returns the value of this {@code AtomicLong} as a {@code double}
* after a widening primitive conversion.
*
* @jls 5.1.2 Widening Primitive Conversions
*/
public double doubleValue() {
return get();
}
private static double toDouble(long l) {
return longBitsToDouble(l);
}
private static long toLong(double delta) {
return doubleToLongBits(delta);
}
}
- 1. Comunicación multiproceso: ¿qué tan bueno es el uso de variables atómicas como AtomicInteger? ¿Por qué no hay AtomicFloat?
- 2. ¿Por qué no hay byte o literales cortos en Java?
- 3. Hibernate, Java: no hay ninguna sesión o sesión se cerró
- 4. ¿no hay una "función" normal en java?
- 5. por qué no hay sizeof en java
- 6. ¿Hay un puerto Java o equivalente de la biblioteca EventStore?
- 7. ¿Hay alguna manera de imitar o en genéricos de Java
- 8. ¿Hay una biblioteca similar a lxml o nokogiri para Java?
- 9. ¿Hay una implementación R para Java o .NET?
- 10. ¿Qué razón hay para que C# o Java tengan lambdas?
- 11. ¿hay alguna manera de usar tr /// (o equivalente) en java?
- 12. ¿Hay utilidades o jarras de reflexión Java de código abierto?
- 13. ¿Hay alguna forma de hacer bitwise-O enumeraciones en Java?
- 14. ¿Hay un método estático Java "nulo o igual" estático?
- 15. Propiedades de Java: ¿exponer o no exponer?
- 16. Java Thread Basura recolectada o no
- 17. ¿Por qué no hay interfaz Hashable en Java
- 18. ¿Hay una biblioteca de geometría para Java? (no JTS)
- 19. Colecciones de Java. ¿Por qué no hay tipos primitivos?
- 20. No hay fuente de Java en Mac OS X
- 21. ¿Por qué no hay interfaces finales en Java?
- 22. ¿Hay constantes para los códigos de idioma en java o en una biblioteca java?
- 23. Scala - Java =? (O Clojure -? = Java)
- 24. ¿Por qué no hay operadores || = o && = en C#?
- 25. ¿Hay bibliotecas o muestras para el agrupamiento WCF no dúplex?
- 26. No hay claves principales o candidato en la tabla referenciada
- 27. ¿Por qué hay insertOrThrow pero no updateOrThrow o deleteOrThrow?
- 28. ¿Hay una versión no instalable de Fiddler, o equivalente?
- 29. No hay UserTransaction JTA disponible: especifique 'userTransaction' o 'userTransactionName'
- 30. ¿Por qué no hay una variable estática local en Java?
No hay una sola. ¿Cuál es tu caso de uso? –
Agregado en Java 8, [DoubleAdder] (http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/DoubleAdder.html) puede ajustarse a sus necesidades. – kuporific