2012-02-20 10 views
12

como sugiere el título, estoy buscando una aplicación de comparación y de intercambio, pero con mayor que la comparación:mayores a las de intercambio de comparación y

if(newValue > oldValue) { 
    oldValue = newValue; 
} 

donde oldValue es un estado global compartida y newValue es privado para cada hilo, sin hacer esto:

synchronized(locker) { 
    if(newValue > oldValue) { 
     oldValue = newValue; 
    }  
} 

porque quiero una solución no bloqueante. Del estudio de los códigos fuente de las demás operaciones no bloqueantes, yo he llegado con esto (suponiendo que los valores son números enteros):

AtomicInteger oldValue; // shared global variable 

... 

public boolean GreaterThanCAS(int newValue) { 

    while(true) { 
     int local = oldValue; 
     if(local == oldValue) { 
      if(newValue > local) { 
       if(oldValue.compareAndSet(local, newValue) { 
        return true; // swap successful 
       } // else keep looping 
      } else { 
       return false; // swap failed 
      } 
     } // else keep looping 
    } 
} 

cuando // else keep looping sucede, significa que otro hilo ha cambiado la oldValue en el ínterin y entonces necesito repetir y volver a intentarlo

¿Es correcta esta implementación (sin hilos)?

+0

Esto sólo está comprobando para ver si la conmutación de hilo se produjo entre la asignación de la variable 'local' y la comprobación para ver si son lo mismo La conmutación de subprocesos podría ocurrir después de su declaración if. Así que no, esto no es seguro para subprocesos, pero sin bloquear no estoy seguro de si encontrará una solución. – Shaded

+0

@Shaded: la llamada 'oldValue.compareAndSwap (localValue)' también devuelve falso si 'oldValue' no es igual a' local', por lo que también se comprueba aquí. – Tudor

+0

No necesita comparar primero la igualdad. Solo "if (newValue> local) oldValue.CAS (local, newValue) else repeat" es suficiente – BegemoT

Respuesta

7

No veo ningún problema con su implementación, siempre que ningún hilo disminuya el valor de AtomicInteger. Si lo hacen, su código está abierto a condiciones de carrera.

Tenga en cuenta que el código puede simplificarse de la siguiente manera:

public boolean GreaterThanCAS(int newValue) { 
    while(true) { 
     int local = oldValue.get(); 
     if(newValue <= local) { 
      return false; // swap failed 
     } 
     if(oldValue.compareAndSet(local, newValue)) { 
      return true; // swap successful 
     } 
     // keep trying 
    } 
} 
+0

Gracias. Tiene razón en que la disminución causaría problemas, pero para mi caso, el valor de 'oldValue' solo puede cambiar al ejecutar esta operación.Gracias también por la sugerencia de simplificación. Ahora que lo pienso, ese 'si' fue realmente redundante. – Tudor

+0

Creo que su código usando '<=' para comparar en un método con 'GreaterThan' en el nombre es un poco extraño. –

+0

@ TomHawtin-tackline: encuentro que esta estructura es más legible que las declaraciones 'if' anidadas originales. Si uno se opone a '<=', uno podría simplemente expresarlo como 'if (! (NewValue> local))'. Personalmente, no encuentro esta versión reformulada más o menos clara que la que puse en la respuesta. – NPE

2

Me gustaría volver a escribirlo a parecerse más a:

while(true) { 
    int local = oldValue.get(); 
    if(newValue > local){ 
     if(oldValue.compareAndSwap(local, newValue) { 
       return true; // swap successful 
     } // else keep looping 
    }else 
     return false; 
} 

La verificación de equivalencia antes de que el cheque más grande que es redundante.

De lo contrario, debería funcionar bien.

10

Desde Java 8 este puede simplificarse con el uso de updateAndGet:

public boolean greaterThanCAS(int newValue) { 
    return oldValue.updateAndGet(x -> x < newValue ? newValue : x) == newValue; 
} 

Tenga en cuenta que esto podría volver cierto también en el caso en que los valores antiguos y nuevos son iguales. Pruebe @Adam's answer si este no es el comportamiento deseado.

+3

'x hengxin

+0

@hengxin, gracias, arreglado. – Vadzim

+0

¿Debería estar usando 'updateAndGet' en su lugar? –

2

@Vadzim, habría comentado tu publicación, pero stackoverflow dice que no tengo suficientes puntos para publicar comentarios. Su respuesta es casi correcta, pero su función siempre será falsa porque getAndUpdate siempre devuelve el valor anterior, o 'x' en su caso. Creo que todo lo que tendría que hacer es reemplazar su última '==' con '<', por ejemplo:

// return true if the assignment was made, false otherwise 
public boolean greaterThanCAS(int newValue) { 
    return oldValue.getAndUpdate(x -> x < newValue ? newValue : x) < newValue; 
} 
+0

Gracias por señalar. Esta respuesta también es correcta, pero he arreglado la mía con cambiando a 'updateAndGet'. Tenga en cuenta que ahora las respuestas difieren en el manejo del caso cuando los valores antiguos y nuevos son iguales. Depende del contexto qué comportamiento se adapta mejor. – Vadzim

Cuestiones relacionadas