2011-09-27 27 views

Respuesta

6

Si estás tan preocupado por la contención usando CAS o synchronized entonces usted podría considerar algo más sofisticado como el JSR propuesta 166E LongAdder (source, javadoc).

Es un contador directo con baja contención en acceso multiproceso. Puede envolver eso para exponer (valor máximo del valor actual del mod). Es decir, no almacene el valor envuelto en absoluto.

1

Puede usar la clase java.util.concurrent.atomic.AtomicInteger para aumentar atómicamente. En cuanto a establecer un límite superior y retroceder al 0, deberá hacerlo externamente ... quizás encapsulando todo esto dentro de su propia clase contenedora.

En realidad, parece que se puede utilizar para comprobar compareAndSet el límite superior, y después volverá a 0.

18

Es fácil de implementar un contador de tales cima AtomicInteger:

public class CyclicCounter { 

    private final int maxVal; 
    private final AtomicInteger ai = new AtomicInteger(0); 

    public CyclicCounter(int maxVal) { 
     this.maxVal = maxVal; 
    } 

    public int cyclicallyIncrementAndGet() { 
     int curVal, newVal; 
     do { 
      curVal = this.ai.get(); 
      newVal = (curVal + 1) % this.maxVal; 
     } while (!this.ai.compareAndSet(curVal, newVal)); 
     return newVal; 
    } 

} 
+0

Me pregunto si esto sería realmente atómica si pierde el hilo de prioridad entre '' 'this.ai.get()' '' y '' '} while (! This.ai.compareAndSet (CURVAL, newal)); '' ' –

+0

@Renato Atrás: Sí, lo haría. La semántica de CAS garantiza eso. –

2

Yo personalmente creo que la solución AtomicInteger es un poco fea, ya que introduce una carrera de condición que significa que su intento de actualización podría "quebrar" y tienen para repetir (iterando dentro del ciclo while) haciendo que el tiempo de actualización sea menos determinista que realizar toda la operación dentro de una sección crítica.

Escribir su propio contador es tan trivial que recomendaría ese enfoque. También es más agradable desde la perspectiva de OO, ya que solo expone las operaciones que puede realizar.

public class Counter { 
    private final int max; 
    private int count; 

    public Counter(int max) { 
    if (max < 1) { throw new IllegalArgumentException(); } 

    this.max = max; 
    } 

    public synchronized int getCount() { 
    return count; 
    } 

    public synchronized int increment() { 
    count = (count + 1) % max; 
    return count; 
    } 
} 

EDITAR

El otro problema que percibo con la solución bucle while es la que da un gran número de hilos que tratan de actualizar el contador que podría terminar con una situación en la que usted tiene varios hilos en vivo girando e intentando actualizar el contador. Dado que solo 1 subproceso tendrá éxito, todos los demás subprocesos fallarán y se repetirán y perderán ciclos de CPU.

+0

También deberías implementar toString() si crees que alguna vez querrás imprimir un contador. – GreenMatt

+0

En cuanto al rendimiento, el entero atómico parece ser más rápido. Ver https://gist.github.com/1245597 – sheki

+0

Adamski, todo el bucle while no es para proporcionar los beneficios que normalmente obtenemos de sincronización. Su solución tendrá el mismo tiempo de actualización no determinista (desde el punto de vista de la persona que llama). – CPerkins

2

Si usa el operador de módulo, podría simplemente incrementar y devolver el módulo. Lamentablemente, el operador de módulo es caro, por lo que recomiendo otras soluciones donde el rendimiento es importante.

public class Count { 
    private final AtomicLong counter = new AtomicLong(); 
    private static final long MAX_VALUE = 500; 
    public long getCount() { 
     return counter.get() % MAX_VALUE; 
    } 
    public long incrementAndGet(){ 
     return counter.incrementAndGet() % MAX_VALUE; 

    } 
} 

Tendría que resolver el caso Long.MAX_VALUE también.

5

Con Java 8

public class CyclicCounter { 

    private final int maxVal; 
    private final AtomicInteger counter = new AtomicInteger(0); 

    public CyclicCounter(int maxVal) { 
     this.maxVal = maxVal; 
    } 

    return counter.accumulateAndGet(1, (index, inc) -> { 
     return ++index >= maxVal ? 0 : index; 
    });  

}

1

tengo que crear un teletipo circular similar para una costumbre lógica Akka enrutamiento, que tenía que ser diferente a la falta de pago para evitar la sobrecarga de la red, ya que mi lógica es solo para elegir la siguiente ruta.

Nota: Copiado del sugerido Java 8 aplicación:

import akka.routing.Routee; 
import akka.routing.RoutingLogic; 
import scala.collection.immutable.IndexedSeq; 

import java.util.concurrent.atomic.AtomicInteger; 

public class CircularRoutingLogic implements RoutingLogic { 

    final AtomicInteger cycler = new AtomicInteger(); 

    @Override 
    public Routee select(Object message, IndexedSeq<Routee> routees) { 
    final int size = routees.size(); 
    return size == 0 ? null : routees.apply(cycler.getAndUpdate(index -> ++index < size ? index : 0)); 
    } 
} 
0

para el contador circular de gran intensidad incrementada en múltiples hilos en paralelo, yo recomendaría usar LongAdder (desde Java 8, consulte la idea central dentro de Striped64.java), porque es más escalable en comparación con AtomicLong. Es fácil adaptarlo a las soluciones anteriores.

Se supone que get operación no es tan frecuente en LongAdder. Cuando llame al counter.get, solicite 'counter.get% max_number'. Sí, la operación de módulo es costosa, pero es infrecuente en este caso de uso, que debería amortizar el costo total de la ejecución.

recordar, sin embargo, que get operación es no bloqueante, ni atómica.

+1

¿LongAdder tiene método get? ¿Te refieres a suma()? sum(), sí, no es atómico, ¿entonces no sería un problema pasar el límite superior a 0? De todos modos, es interesante ver la implementación * real * ... –