Cómo forzar a un subproceso de Java a cerrar una conexión de base de datos local del subproceso

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

Pregunta

Cuando se utiliza una conexión de base de datos local de subproceso, se requiere el cierre de la conexión cuando el subproceso existe.

Esto sólo puedo hacer si puedo anular el método run() del hilo de llamada.Incluso esa no es una gran solución, ya que en el momento de la salida, no sé si ese hilo alguna vez abrió una conexión.

De hecho, el problema es más general:Cómo forzar a un hilo a llamar a algún método de finalización de un objeto local del hilo cuando sale.

Miré las fuentes de Java 1.5 y descubrí que el mapa local del hilo está configurado en nulo, lo que eventualmente hará que la recolección de basura llame finalizar(), pero no quiero contar con el recolector de basura.

La siguiente anulación parece inevitable para asegurarse de que una conexión de base de datos esté cerrada:

@Override 
public void remove() {
    get().release(); 
    super.remove(); 
}

dónde liberar() cierra la conexión a la base de datos, si se ha abierto.Pero no sabemos si el hilo alguna vez ha usado este hilo local.Si este hilo nunca ha llamado a get(), entonces hay una gran pérdida de esfuerzo aquí: ThreadLocal.initialValue() se llamará, se creará un mapa en este hilo, etc.

-

Más aclaraciones y ejemplos según el comentario de Thorbjørn:

java.lang.ThreadLocal es un tipo de fábrica para un objeto que está vinculado a un hilo.Este tipo tiene un captador para el objeto y un método de fábrica (normalmente escrito por el usuario).Cuando se llama al captador, llama al método de fábrica solo si este hilo nunca lo ha llamado antes.

Usando Hilo local permite a un desarrollador vincular un recurso a un hilo incluso si el código del hilo fue escrito por un tercero.

Ejemplo:Digamos que tenemos un tipo de recurso llamado Mi tipo y queremos tener uno y solo uno por hilo.

Defina en la clase de uso:Private Static ThreadLocal ResourceFactory = new ThreadLocal () {@Override MyType InitialValue () {return new myType ();}}

Usar en contexto local en esta clase:public void somemethod () {mytype recursce = resourgeFactory.get ();recurso.useResource();}

conseguir() puede llamar valor inicial() sólo una vez en el ciclo de vida del hilo de llamada.En ese momento una instancia de Mi tipo se crea una instancia y se vincula a este hilo.Llamadas posteriores a conseguir() por este hilo se refieren nuevamente a este objeto.

El ejemplo de uso clásico es cuando Mi tipo es un formateador de texto/fecha/xml no seguro para subprocesos.

Pero normalmente no es necesario liberar o cerrar estos formateadores, las conexiones de bases de datos sí, y yo estoy usando java.lang.ThreadLocal tener una conexión de base de datos por subproceso.

La manera en que lo veo, java.lang.ThreadLocal es casi perfecto para eso.Casi porque no hay forma de garantizar el cierre del recurso si el hilo que llama pertenece a una aplicación de terceros.

Necesito vuestros cerebros escuderos:Al extender java.lang.ThreadLocal Logré vincular una conexión de base de datos para cada subproceso, para su uso exclusivo, incluidos los subprocesos que no puedo modificar ni anular.Me las arreglé para asegurarme de que las conexiones se cerraran en caso de que el hilo muera debido a una excepción no detectada.

En caso de salida normal del hilo, el recolector de basura cierra la conexión (porque Mi tipo anula el finalizar()).En realidad, esto sucede bastante rápido, pero no es lo ideal.

Si por mí fuera, habría habido otro método en el java.lang.ThreadLocal:

protected void release() throws Throwable {}

Si este método existiera en java.lang.ThreadLocal, llamado por JVM ante la salida/muerte de cualquier hilo, luego, en mi propia anulación, podría cerrar mi conexión (y el redentor habría venido a Sión).

En ausencia de tal método, estoy buscando otra forma de confirmar el cierre.Una forma que no dependerá de la recolección de basura de JVM.

Gracias

¿Fue útil?

Solución

Si usted es de una disposición sensible, mirar a otro lado.

No esperaría que esta a escala muy bien; que efectivamente duplica el número de procesos en el sistema. Puede haber algunos casos de uso en los que es aceptable.

public class Estragon {
  public static class Vladimir {
    Vladimir() { System.out.println("Open"); }
    public void close() { System.out.println("Close");}
  }

  private static ThreadLocal<Vladimir> HOLDER = new ThreadLocal<Vladimir>() {
    @Override protected Vladimir initialValue() {
      return createResource();
    }
  };

  private static Vladimir createResource() {
    final Vladimir resource = new Vladimir();
    final Thread godot = Thread.currentThread();
    new Thread() {
      @Override public void run() {
        try {
          godot.join();
        } catch (InterruptedException e) {
          // thread dying; ignore
        } finally {
          resource.close();
        }
      }
    }.start();
    return resource;
  }

  public static Vladimir getResource() {
    return HOLDER.get();
  }
}

Mejor manejo de errores y así sucesivamente se deja como ejercicio para el implementador.

También puede echar un vistazo a el seguimiento de los hilos / recursos en un ConcurrentHashMap con otro sondeo hilo isAlive . Pero esa solución es el último recurso de los desesperados -. Objetos probablemente terminará siendo verificados con demasiada frecuencia o muy rara vez

No puedo pensar en otra cosa que no implica la instrumentación. AOP podría funcionar.

La agrupación de conexiones sería mi opción preferida.

Otros consejos

Envuelva su Ejecutable con un nuevo Ejecutable con un

try {
  wrappedRunnable.run();
} finally {
  doMandatoryStuff();
}

construcción, y deja que sea ejecutado en su lugar.

Se podría incluso convertirlo en un método, por ejemplo:

  Runnable closingRunnable(Runnable wrappedRunnable) {
    return new Runnable() {
      @Override
      public void run() {
        try {
          wrappedRunnable.run();
        } finally {
          doMandatoryStuff();
        }
      }
    };
  }

y llamar a ese método que pasa en el ejecutable que busca.

También es posible que desee considerar el uso de un albacea en su lugar. Hace que sea mucho más fácil de manejar y runable callables.

Si usted hace uso de un ExecutorService, se puede usar como executor.submit(closingRunnable(normalRunnable))

Si usted sabe que usted va a apagar todo su ExecutorService y desea conexiones para cerrar en ese punto, se puede establecer una fábrica de hilos que también lo hace el cierre "después de que todas las tareas se llevan a cabo y apagado pidió al ejecutor", ejemplo:

  ExecutorService autoClosingThreadPool(int numThreads) {
    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(numThreads, numThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); // same as Executors.newFixedThreadPool
    threadPool.setThreadFactory(new ThreadFactory() {
      @Override
      public Thread newThread(Runnable r) {
        return new Thread(closingRunnable(r)); // closes it when executor is shutdown
      }
    });
    return threadPool;
  }

En cuanto a si o no doMandatoryStuff puede saber si alguna vez se abrió la conexión o no con anterioridad, una cosa que viene a la mente es tener un segundo ThreadLocal que simplemente rastrea si se abre o no (por ejemplo: cuando la conexión es abierto, conseguir a continuación, establecer un AtomicInteger a 2, en el momento de la limpieza, comprobar para ver si todavía está en su defecto, por ejemplo, 1 ...)

La práctica normal JDBC es que se cierra la Connection (y Statement y ResultSet) en el mismo bloque de método como desee adquirió.

En el código:

Connection connection = null;

try {
    connection = getConnectionSomehow();
    // Do stuff.
} finally {
    if (connection != null) {
        try {
            connection.close();
        } catch (SQLException e) {
            ignoreOrLogItButDontThrowIt(e);
        }
    }
}

Con esto en mente, su pregunta me hace pensar que hay algo mal en su diseño. La adquisición y el cierre de ese tipo de recursos costosos y externos en el alcance más corto salvará la aplicación de posibles fugas de recursos y los accidentes.

Si su intención original era mejorar el rendimiento de conexión, entonces usted necesita para echar un vistazo por agrupación de conexiones . Se puede utilizar, por ejemplo, la API C3P0 para esto. O si se trata de una aplicación web, utilizar las instalaciones de conexión de la puesta en común del servidor de aplicaciones incorporadas en el sabor de un DataSource. Consulte la documentación del servidor de aplicaciones específicas para más detalles.

No entiendo realmente por qué no está usando la agrupación de conexiones tradicionales. Pero voy a suponer que tenga sus razones.

¿Cuánta libertad tiene usted? Como algunos marcos DI hacen apoyar ciclos de vida de objeto y las variables de rosca de ámbito de (todo muy bien por proxy). Podría utilizar una de ellas? Creo que la primavera haría todo esto fuera de la caja, mientras que Guice necesitaría una tercera parte lib para manejar los ciclos de vida y alcances de rosca.

A continuación cuánto control tiene usted ya sea en la creación de la variable ThreadLocal o la creación de hilos? Creo que te tiene el control completo sobre la ThreadLocal pero limitado a ninguno en la creación de hilos?

¿Podría utilizar programación orientada a aspectos de controlar para los nuevos threads ejecutables o extender el método run () para incluir la limpieza? También tendrá que extender el ThreadLocal para que pueda registrarse a sí mismo.

Había que abrir la conexión una vez, por lo que también tiene que manejar el cierre en el mismo lugar. Según el entorno de los hilos pueden ser reutilizados y no se puede esperar que el hilo sea recolectado antes del cierre de la aplicación.

Creo que en el caso general no hay una buena solución para esto, aparte del clásico: Código que obtiene el recurso tiene que ser responsable de su cierre

.

En el caso concreto, si está llamando a las discusiones a este grado se podía pasar en la conexión con sus métodos en el inicio de la rosca, ya sea con un método que toma un parámetro personalizado o por medio de algún tipo de inyección de dependencias. A continuación, ya que tienes el código que suministra la conexión, usted tiene el código que lo elimina.

Una inyección de dependencia basado en anotaciones podría trabajar aquí, como un código que no requiere la conexión no va a conseguir uno y por lo tanto no tendría que estar cerrado, pero suena como su diseño es demasiado largo para adaptar algo de esa manera.

Reemplazar el método get () en ThreaedLocal de manera que establece una propiedad de lista en la subclase. Esta propiedad puede ser fácilmente consultada para determinar si el método get () había sido llamado por un hilo en particular. A continuación, puede acceder al ThreadLocal para limpiarlo en ese caso.

Actualización En respuesta al comentario

Lo que hicimos fue

@Override
public void run() {
  try {
    // ...
  } finally {
    resource.close();
  }
}

Básicamente, sólo siempre (posiblemente abierta y después) se cierra para todos los caminos a través del hilo. En caso de que ayuda a nadie por ahí:)

Estoy buscando al mismo problema. Parece que hasta ahora tiene que usar finalize (), a pesar de que ahora es obsoleto. A medida que las tareas son sometidos a algún Ejecutor, nunca se sabe cuando un thread sale exactamente, a menos que el Ejecutor muestra, eso significa que tienes el control del ejecutor en cierta medida.

Por ejemplo, si se construye el Ejecutor extendiendo ThreadPoolExecutor, es posible anular el afterExecution () y los terminados () métodos para lograrlo. El anterior para un hilo que sale anormalmente mientras que el último para normalmente.

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