¿Deberían bloquearse los captadores/definidores de tipos primitivos con ReadWriteLock en una aplicación multiproceso?

StackOverflow https://stackoverflow.com/questions/5014266

Pregunta

Tengo una clase Java que se usa en una aplicación multiproceso.Es muy probable que haya acceso simultáneo.Varias operaciones de lectura simultáneas no deberían bloquearse, por lo que estoy usando un bloqueo ReadWrite.

class Example {
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private int i;
    private boolean b;

    public int getI() {
      lock.readLock().lock();
      final int tmp = i;
      lock.readLock().unlock(),
      return tmp;
    }

    public void setI( final int i ) {
      lock.writeLock().lock();
      this.i = i;
      lock.writeLock().unlock();
    }

    public boolean getB() {
      lock.readLock().lock();
      final boolean tmp = b;
      lock.readLock().unlock(),
      return tmp;
    }

    public void setB( final boolean b ) {
      lock.writeLock().lock();
      this.b = b;
      lock.writeLock().unlock();
    }
}

Por simplicidad omití el try...finally bloques alrededor de las cerraduras en este ejemplo.

Me pregunto si es necesario (o digamos recomendado) bloquear/sincronizar captadores y definidores de tipos primitivos.Sé que las operaciones de asignación y devolución en Java son atómicas.Sin embargo, al usar estos bloqueos, no me aseguro de que cada acceso obtenga el último valor (equivale a usar volatile)?

¿Y si los primitivos fueran double o long?

¿Fue útil?

Solución

Eso depende.

Sin embargo, tenga en cuenta que normalmente necesita sincronizar operaciones a un nivel más general, por ejemplo, este:

Example e = ...;

synchronized (e) {
    e.setI(e.getI() + 10);
}

Para tales escenarios, sus cerraduras internas son redundantes.Por lo tanto, quizás sería mejor aplicar la sincronización externa cuando utilice estos objetos, en lugar de la sincronización interna.

Otros consejos

tienes algo como AtomicInteger en Java, que funciona bien con aplicaciones multiproceso.

Pregúntese si esta clase se puede implementar como una clase inmutable.Será más fácil trabajar con él y será inherentemente seguro para subprocesos.No tendrá que preocuparse por la concurrencia, los bloqueos, la sincronización, etc.

Ejemplo de una clase inmutable:

final class Example {
    private final int i;
    private final boolean b;

    public Example(int i, boolean b){
        this.i = i ;
        this.b = b;
    }

    public int getI() {
        return i;
    }

    public boolean getB() {
        return b;
    }
}

Diseñaría su aplicación para que no tenga acceso simultáneo a un tipo de datos sin procesar como este.Es probable que agregar un bloqueo de tan bajo nivel ralentice tanto su aplicación que no vale la pena utilizar múltiples subprocesos en su aplicación.

p.ej.Supongamos que tiene un sistema de 32 núcleos que se escala perfectamente y se ejecuta 32 veces más rápido que con 1 núcleo.Sin embargo, un acceso a un campo sin bloqueo tarda 1 ns, con bloqueo tarda 1 us (1000 ns), por lo que al final su aplicación podría tardar ~30 veces más lento.(1000 más lento / 32 más rápido) Si tiene solo 4 núcleos, podría ser cientos de veces más lento, anulando el propósito de tener subprocesos múltiples en primer lugar.EN MI HUMILDE OPINIÓN.

No es necesario bloquear/sincronizar captadores y definidores de tipos primitivos; etiquetarlos como volátiles es suficiente en la mayoría de los casos (aparte de double y long como usted menciona)

Como menciona una de las publicaciones anteriores, debe tener en cuenta las secuencias de lectura y actualización, algo así como incrementI(int num) que probablemente invocará getI() y setI(); en este caso, podría agregar un 'incrementI sincronizado(int num)' a su clase de ejemplo.Luego, el bloqueo se realiza en un nivel superior, lo que reduce la necesidad de bloqueos de lectura y escritura separados y es compatible con OO ya que los datos y el comportamiento permanecen juntos.Esta técnica es aún más útil si está leyendo/actualizando varios campos a la vez.

Aunque si simplemente estás leyendo/escribiendo/actualizando un campo a la vez, entonces las clases AtomicXX son más apropiadas

No debe utilizar el bloqueo para tipos primitivos, cadenas (son inmutables) y tipos seguros para subprocesos (como colecciones de paquetes "concurrentes").

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top