2010-04-26 10 views
7

Todavía soy un estudiante universitario que solo trabaja a tiempo parcial y por eso siempre trato de ser consciente de mejores formas de hacer las cosas. Recientemente tuve que escribir un programa para el trabajo donde el hilo principal del programa generaba hilos de "tareas" (para cada registro de "tarea" db) que realizaba algunas operaciones y luego actualizaba el registro para decir que había terminado. Por lo tanto, necesitaba un objeto de conexión de base de datos y los objetos PreparedStatement en o disponibles para los objetos ThreadedTask.¿Es correcto este uso de PreparedStatements en un subproceso en Java?

Esto es más o menos lo que terminé escribiendo, ¿está creando un objeto PreparedStatement por cada hilo de un desperdicio? Pensé estática PreparedStatments podría crear condiciones de carrera ...

versión
 
Thread A stmt.setInt(); 
Thread B stmt.setInt(); 
Thread A stmt.execute(); 
Thread B stmt.execute(); 

excelentes calificaciones nunca se Execed ..

¿Este hilo de seguridad? ¿Crear y destruir PreparedStatement objetos que son siempre los mismos no es una gran pérdida?

public class ThreadedTask implements runnable { 
    private final PreparedStatement taskCompleteStmt; 

    public ThreadedTask() { 
     //... 
     taskCompleteStmt = Main.db.prepareStatement(...); 
    } 

    public run() { 
     //... 
     taskCompleteStmt.executeUpdate(); 
    } 
} 

public class Main { 
    public static final db = DriverManager.getConnection(...); 
} 
+2

No guarde la conexión, obtenga la conexión cuando la necesite. Si necesita un rendimiento de bettr use un grupo de conexiones. AS Thilo dijo a continuación no compartir cosas entre subprocesos. –

Respuesta

16

Creo que no es una buena idea compartir las conexiones de la base de datos (y las declaraciones preparadas) entre subprocesos. JDBC no requiere que las conexiones sean seguras para hilos, y yo esperaría que la mayoría de los controladores no lo sean.

Dé a cada hilo su propia conexión (o sincronícese en la conexión para cada consulta, pero eso probablemente anule el propósito de tener múltiples hilos).

¿Crear y destruir objetos PreparedStatement que son siempre los mismos no es un gran desperdicio?

Realmente no. La mayor parte del trabajo ocurre en el servidor, y se almacenará en caché y se reutilizará allí si usa la misma instrucción SQL. Algunos controladores JDBC también admiten el almacenamiento en caché de sentencias, por lo que incluso el identificador de declaración del lado del cliente se puede reutilizar.

Sin embargo, podría ver una mejora sustancial mediante el uso de consultas por lotes en lugar de (o además de) varios subprocesos. Prepare la consulta una vez y ejecútela para obtener una gran cantidad de datos en un solo lote grande.

3

Lo mejor es usar un grupo de conexiones y obtener cada subproceso para solicitar una conexión desde el grupo. Cree sus declaraciones sobre la conexión que le entregan, recuerde cerrarla y libérela al grupo cuando haya terminado. El beneficio de utilizar el grupo es que puede aumentar fácilmente la cantidad de conexiones disponibles si encuentra que la concurrencia de subprocesos se está convirtiendo en un problema.

6

El threadsafety no es el problema aquí. Todo se ve sintáctica y funcionalmente bien y debería funcionar durante aproximadamente media hora. La fuga de recursos es, sin embargo, el problema real aquí. La aplicación se bloqueará después de aproximadamente media hora porque nunca los cierras después de su uso. La base de datos, a su vez, tarde o temprano cerrará la conexión para que pueda reclamarla nuevamente.

Dicho esto, no necesita preocuparse por el almacenamiento en caché de las declaraciones preparadas. El controlador JDBC y el DB se encargarán de esta tarea. Preocúpese por la fuga de recursos y haga que su código JDBC sea lo más sólido posible.

public class ThreadedTask implements runnable { 
    public run() { 
     Connection connection = null; 
     Statement statement = null; 
     try { 
      connection = DriverManager.getConnection(url); 
      statement = connection.prepareStatement(sql); 
      // ... 
     } catch (SQLException e) { 
      // Handle? 
     } finally { 
      if (statement != null) try { statement.close(); } catch (SQLException logOrIgnore) {} 
      if (connection != null) try { connection.close(); } catch (SQLException logOrIgnore) {} 
     } 
    } 
} 

Para mejorar la conexión de rendimiento, hacer uso de un conjunto de conexiones como c3p0 (esto, por cierto, no significa que se puede cambiar la forma en la forma de escribir el código JDBC, siempre adquirir y cerrar los recursos en el alcance más corto posible en un bloque try-finally).

+1

Mire Apache Commons DBUtils para obtener una envoltura liviana que se encargue de todos los intentos/capturas. – Thilo

+0

También podemos simplemente usar JPA. Más conveniente no puede ser. – BalusC

+0

Si está utilizando Java 7+: prefiera usar "intentar con bloques de recursos". En ese caso, no puede olvidarse de cerrar la conexión y la declaración. –

Cuestiones relacionadas