2010-06-02 18 views
7

¿Cómo podemos usar AtomicInteger para una generación de secuencia limitada? Digamos que el número de secuencia debe estar entre 1 y 60. Una vez que la secuencia llega a 60 debe comenzar nuevamente desde 1. Escribí este código aunque no estoy seguro ¿Si esto es seguro o no?AtomicInteger para generación de secuencia limitada

public int getNextValue() 
{ 
int v; 
do 
{ 
    v = val.get(); 
    if (v == 60) 
    { 
    val.set(1); 
    } 
} 
    while (!val.compareAndSet(v , v + 1)); 
    return v + 1; 
    } 

Respuesta

14

Usted podría hacer

return val.getAndIncrement() % 60; 

A menos que usted está preocupado con el número entero superior a max-valor (2147483647). Si esto es una preocupación, se puede echar un vistazo a la implementación getAndIncrement:

public final int getAndIncrement() { 
    for (;;) { 
     int current = get(); 
     int next = current + 1; 
     if (compareAndSet(current, next)) 
      return current; 
    } 
} 

Todo lo que tiene que cambiar es la línea int next... a algo como:

int next = (current + 1) % 60; 

Vaya. Esto recorre 0-> 59. Necesitabas 1-> 60, así que agrega uno al valor de retorno para obtener el resultado deseado.

+0

+1. Es realmente útil – satish

+0

Si encuentra una respuesta realmente útil, haga clic en la marca de verificación para aceptarla. – naiad

+1

Qué aplicación más extraña. Básicamente dice "agrega 1 si aún no ha cambiado, de lo contrario sigue intentándolo". ¿No podría eso teóricamente dar como resultado un ciclo infinito? –

0

No, no es hilo-seguro - no debe llamar set dentro de un ciclo:

int value, next; 
do { 
    value = val.get(); 
    next = (value == 60) ? 1 : (value + 1); 
} while (!val.compareAndSet(value, next); 
return next; 
1

Si realiza el método synchronized entonces será multi-hilo, siempre que el val se accede en ninguna otra parte. El enfoque es sin embargo un poco engorroso, me gustaría volver a escribir de la siguiente manera:

public synchronized int getNextValue() { 
    val.compareAndSet(60, 0); // Set to 0 if current value is 60. 
    return val.incrementAndGet(); 
} 

Esto da 1 hasta el 60 de vuelta incluido. Si realmente necesita 1 hasta con 59, entonces reemplace 60 por 59.

0

¿Alguna razón en particular para usar AtomicInteger aquí en lugar de solo un método simple sincronizado?

¿Qué tal algo tan simple como la siguiente:

private int val=1; 

public synchronized int getNextValue() { 
int v=val; 
val = (val==60) ? 1 : (val+1); 
return v; 
} 
+0

p.s. nada en contra de AtomicIntegers, pero creo que siempre es bueno hacer las cosas lo más simple posible en aras del mantenimiento a largo plazo .... – mikera

+0

Gracias por la respuesta. Estaba buscando un algoritmo de no bloqueo basado en el algoritmo de Bloqueo. – satish

+0

AtomicInteger es mucho más rápido que la sincronización si hay mucha contención (hasta un factor de 10). – starblue

0

respuesta rápida, no seguro para subprocesos. La prueba y el conjunto deben ser atómicos, a menos que sincronices todo el método. Tenga en cuenta que val.get() y la prueba de v no son atómicas. Si el hilo se produce después de v = val.get() obtendrá dos llamadas con el mismo número de secuencia.

Además, si el compareAndSet falla, nunca cambia los valores, será un bucle infinito.

AtomicInteger tiene una llamada getAndIncrement() . Eso te dará un valor limpio para volver.

El laminado es un poco más complicado. Una solución es modificar el valor de retorno. Algo así:

int v = val.getAndIncrement(); 
return (v % 60) + 1; 

Dado que cada subproceso tiene una copia local de v, podemos hacer algunas operaciones matemáticas con seguridad y devolver el valor. Hay un punto de fricción si obtienes un desbordamiento. Dependiendo de la frecuencia con que genere un número de secuencia, esto puede o no ser un problema.

+0

La frecuencia de la generación es bastante alta, probablemente cada 50 ms. – satish

+0

Con 20 llamadas por segundo (una llamada cada 50ms) obtendría una renovación después de aproximadamente 3.4 años. Probablemente no tan mal, dependiendo de tu aplicación. Si eso va a ser un problema, un bloqueo sincronizado es probablemente la solución más fácil. Asegúrese de que este código sea un verdadero cuello de botella antes de volverse loco optimizándolo. Sospecho que hay una fruta más pequeña que una tarea que se ejecuta cada 50 ms. –

3

Puede hacer esto en una sola línea usando Java 8.

AtomicInteger counter = new AtomicInteger(); 

public int getNextValue() { 
    return counter.updateAndGet(n -> (n >= 60) ? 1 : n + 1); 
} 
Cuestiones relacionadas