Tengo una clase DAO que tiene varios métodos que actualizan las tablas de base de datos. Estos métodos se llaman en muchos lugares diferentes a lo largo del código base, en muchos órdenes diferentes.

public class Dao {
  public synchronized updateA()
  public synchronized updateB()
  public synchronized updateC()
  public synchronized getA()
  public synchronized getB()
  public synchronized getC()
}

Tengo un problema en el que Class1 quiere llamar a getA(), getB() y getC() en secuencia. Las tres tablas A, B y C están relacionadas, por lo que necesito sincronizar su estado en un momento determinado.

Sin embargo, después de que Class1 llame a getA() y antes de que llame a getB(), Class2 en otro hilo interviene y llama a updateB(), lo que rompe cosas.

¿Es posible bloquear toda la clase DAO mientras hay algún hilo allí y solo desbloquearlo cuando esté terminado?

Intenté poner un ReentrantLock estático en la clase DAO y bloquearlo desde Class1, pero no estoy seguro de cómo ir desde allí.

Llegué tan lejos como lo siguiente:

public class Class1 {
  public void check() {
    dao.daoLock.lock();
    dao.getA();
    dao.getB();
    dao.getC();
    dao.daoLock.unlock();
  }
}

public class Dao {
  public static final daoLock = new ReentrantLock();
  public synchronized updateA() {
    daoLock.lock();
    // Do stuff...
  }
  public synchronized updateB() {
    daoLock.lock();
    // Do stuff...
  }
  public synchronized updateC() {
    daoLock.lock();
    // Do stuff...
  }
  public synchronized getA() {
    daoLock.lock();
    // Do stuff...
  }
  public synchronized getB() {
    daoLock.lock();
    // Do stuff...
  }
  public synchronized getC() {
    daoLock.lock();
    // Do stuff...
  }
}

No estoy seguro de la ubicación del desbloqueo. Si lo pongo en cada uno de los métodos de la clase DAO, permitirá que entren otros hilos, ¿no es así?

¿Existe una mejor solución aquí?

0
karoma 23 ago. 2016 a las 12:24

2 respuestas

La mejor respuesta

Está intentando sincronizar el acceso a la base de datos en el lado del cliente, que es fundamentalmente el lugar incorrecto para hacerlo. ¿Qué pasa si otro cliente escribe mientras tanto?

Es mejor dejar este tipo de sincronización en la propia base de datos, utilizando transacciones (MySQL, PostgreSQL ).

Incluso si no tiene varios clientes y nunca los tendrá, usar transacciones es una mejor solución. De lo contrario, todos sus hilos se bloquearán entre sí incluso si solo están haciendo lecturas, lo que en principio puede suceder al mismo tiempo sin problemas.

3
Thomas 23 ago. 2016 a las 09:30

Además de la buena respuesta de Thomas, hay otro aspecto aquí que vale la pena considerar: interfaces deben escribirse de una manera que facilite hacer lo correcto; y es difícil hacer algo incorrecto.

Significado: si alguna operación requiere que llame a (), b (), c () en secuencia, y de una "manera bloqueada general", entonces: en lugar de tener a, b, c en su interfaz, proporciona exactamente < strong> one método abc () ... que adquiere el bloqueo, llama a, b, cy libera el bloqueo.

Por lo tanto, debe retroceder y echar un vistazo a la interfaz de su clase desde un SOLID < / a> perspectiva; o más específico, considerándolo desde el lado del "principio de responsabilidad única".

En otras palabras: tener una clase que proporciona una gran cantidad de métodos completamente "disyuntivos"; donde algunos de ellos incluso necesitan ser "usados ​​juntos, de una manera muy específica" que es un claro olor a diseño; otra indicación de que es mejor que dé un paso atrás y revise este diseño.

1
GhostCat 23 ago. 2016 a las 09:35