Comment faire pour forcer un fil de Java pour fermer un fil-connexion de base de données locales

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

Question

Lors de l'Utilisation d'un thread local, connexion de base de données, la fermeture de la connexion est requise lorsque le thread existe.

Cela je ne peux le faire si je peux remplacer la méthode run() du thread appelant.Même cela n'est pas une solution idéale, car au moment de la sortie, je ne sais pas si une connexion a jamais été ouvert par ce thread.

Le problème est en fait plus général:Comment faire pour forcer un thread pour appeler quelques-finalisation de la méthode d'un thread local de l'objet lorsqu'il la quitte.

J'ai regardé les sources de java 1.5 et a constaté que le fil carte locale est définie à null, ce qui finira par causer de la collecte des déchets appeler finalize(), mais je ne veux pas compter sur le garbage collector.

La substitution suivante semble inévitable de faire en sorte qu'une connexion de base de données est fermée:

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

release() ferme la connexion à la base, si il a été ouvert.Mais nous ne savons pas si le fil n'a jamais utilisé ce fil local.Si get() n'a jamais été appelé par ce fil, alors il est tout à fait un gaspillage d'efforts ici: ThreadLocal.initialValue() sera appelé, une carte sera créé sur ce fil, etc.

-

Des précisions et des exemple comme par Thorbjørn commentaire:

java.lang.ThreadLocal est un type de l'usine pour un objet qui est lié à un thread.Ce type a un getter pour l'objet et une méthode de fabrique (généralement écrit par l'utilisateur).Lorsque la lecture est appelée, elle appelle la méthode de fabrique seulement si il n'a jamais été appelé avant par ce fil.

À l'aide de ThreadLocal permet à un développeur de lier une ressource à un fil, même si le code de thread a été écrit par un tiers.

Exemple:Disons que nous avons un Type de ressource appelée MyType et nous voulons avoir un et un seul par thread.

Définir à l'aide de la classe:private static ThreadLocal resourceFactory = new ThreadLocal(){ @override protégé MyType initialValue(){ de retour de nouveau MyType();} }

Utilisation dans le contexte local dans cette classe:public void someMethod(){ MyType resource = resourceFactory.get();de la ressource.useResource();}

get() peuvent appeler initialValue() une seule fois dans le cycle de vie du thread appelant.À ce point, une instance de MyType est instanciée et liée à ce fil.Les appels suivants à get() par ce fil se référer à nouveau à cet objet.

L'utilisation classique, par exemple, lorsque MyType est fil-dangereux texte/date/xml formateur.

Mais ces formateurs n'est généralement pas nécessaire d'être libérés ou fermé, de connexions de base de données le faire et je suis en utilisant java.lang.ThreadLocal pour avoir une connexion de base de données par fil.

La façon dont je le vois, java.lang.ThreadLocal est presque parfaite.Presque, car il n'y a aucun moyen de garantir la fermeture de la ressource si le thread appelant appartient à une application tierce.

J'ai besoin de votre cerveau squires:En étendant java.lang.ThreadLocal J'ai réussi à lier une connexion de base de données pour chaque thread, pour son usage exclusif - y compris les fils que je ne peux pas les modifier ou de les remplacer.J'ai réussi à faire assurez-vous que les connexions fermé dans un cas, le thread meurt sur uncaught exception.

Dans le cas de la normale de la sortie de thread, le garbage collector ferme la connexion (parce que MyType remplace la finalize()).En réalité, il arrive assez rapidement, mais ce n'est pas l'idéal.

Si j'ai eu mon chemin, il y aurait eu une autre méthode sur la java.lang.ThreadLocal:

protected void release() throws Throwable {}

Si cette méthode existe sur java.lang.ThreadLocal, appelé par la JVM sur n'importe quel fil de sortie et de la mort, puis, dans ma propre remplacer des que je le pouvais fermer ma connexion (et le rédempteur serait venu à Sion).

En l'absence d'une telle méthode, je suis à la recherche d'un autre moyen de confirmer la fermeture.Façon à ne pas se fier à la JVM de collecte des ordures.

Merci

Était-ce utile?

La solution

Si vous êtes sensibles disposition, regarder loin maintenant.

Je ne m'attends pas à ce à l'échelle très bien;il a pour effet de doubler le nombre de threads dans le système.Il peut y avoir certains cas d'utilisation où il est acceptable.

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();
  }
}

Meilleure gestion des erreurs et ainsi de suite est laissé comme exercice pour le responsable de l'implémentation.

Vous pourriez aussi avoir un oeil sur le suivi de l'threads/ressources ConcurrentHashMap avec un autre thread d'interrogation isAlive.Mais c'est la solution de dernier recours, des désespérés, des objets va probablement finir par être vérifié, trop souvent, ou trop rarement.

Je ne peux pas penser à autre chose qui n'implique pas de l'instrumentation.AOP pourrait fonctionner.

Le regroupement de connexion serait pour moi une bonne option.

Autres conseils

Enveloppez votre Exécutable avec un nouveau Praticable avec un

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

de la construction, et QU'elle soit exécutée à la place.

Vous pourriez même en faire une méthode, ex:

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

et appelez la méthode de passage dans l'exécutable que vous cherchez.

Vous pouvez aussi envisager d'utiliser un Exécuteur testamentaire à la place.Il est beaucoup plus facile à gérer Runable et Callables.

Si vous vous servez d'un ExecutorService, vous pouvez l'utiliser comme executor.submit(closingRunnable(normalRunnable))

Si vous savez que vous allez être l'arrêt de l'ensemble de votre ExecutorService et souhaitez les connexions à se fermer à ce point, vous pouvez définir un fil de l'usine, qui fait aussi la fermer "une fois toutes les tâches sont effectuées et l'arrêt appelé l'exécuteur", par exemple:

  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 termes de savoir si ou de ne pas doMandatoryStuff peut savoir si ou non la connexion n'a jamais été ouvert ou pas déjà, une chose qui vient à l'esprit est d'avoir un deuxième ThreadLocal que juste des pistes s'il a été ouvert ou non (ex:lorsque la connexion est ouverte, obtenir ensuite définir une AtomicInteger à 2, au temps de nettoyage, vérifier pour voir si il est toujours à sa valeur par défaut, disons, 1...)

La normale JDBC pratique, c'est que vous fermez la Connection (et Statement et ResultSet dans le même méthode de bloc que vous auriez acquis.

Dans le code:

Connection connection = null;

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

En gardant cela à l'esprit, votre question me fait penser qu'il ya quelque chose de mal dans votre conception.L'acquisition et la fermeture de ce genre de cher et de ressources externes dans les plus brefs portée permettra d'économiser de l'application à partir de ressources potentielles fuites et les accidents.

Si votre intention initiale était d'améliorer les performances de la connexion, alors vous avez besoin de prendre un coup d'oeil pour le regroupement de connexion.Vous pouvez utiliser par exemple le C3P0 API pour cela.Ou si c'est une webapplication, utiliser le serveur d'applications est builtin le regroupement de connexion des installations dans la saveur d'un DataSource.Consulter le serveur d'applications spécifiques à la documentation pour plus de détails.

Je ne comprends vraiment pas pourquoi vous n'êtes pas en utilisant des liens traditionnels de la mutualisation.Mais je suppose que vous avez vos raisons.

Quelle liberté avez-vous?Comme certains DI cadres en charge d'objet de la vie et les cycles de filetage-portée des variables (toutes joliment proxy).Pourriez-vous utiliser l'un d'eux?Je pense que le Printemps serait-ce, hors de la boîte, tandis que Guice aurait besoin d'un 3ème parti lib pour gérer les cycles de vie et de fil étendues.

Suivant le degré de contrôle que vous avez sur la création de la ThreadLocal variable ou la création de threads?Je devine que vous avez le contrôle complet sur la ThreadLocal, mais limitée à l'absence sur la création de threads?

Pourriez-vous utiliser la programmation orientée Aspects à surveiller pour les nouveaux Exécutables ou des Fils de l'extension de la méthode run() pour inclure le nettoyage?Vous aurez également besoin d'étendre la ThreadLocal de sorte qu'il peut s'inscrire lui-même.

Vous avez eu à ouvrir la connexion une fois, si vous aussi avez à gérer la fermeture à la même place.En fonction de votre environnement, les threads peuvent être réutilisés et vous ne pouvez pas attendre le thread pour être nettoyée avant l'arrêt de l'application.

Je pense que dans le cas général, il n'y a pas de bonne solution pour ce faire, d'autres que le classique:Code de la ressource doit être responsable de la fermeture.

Dans le cas précis, si vous appelez de threads à ce degré, vous pouvez passer à la connexion à vos méthodes au début du fil, soit avec une méthode qui prend un paramètre personnalisé ou par l'intermédiaire de l'Injection de Dépendance.Alors, puisque vous avez le code, qui fournit la connexion, vous avez le code qui supprime.

Une Injection de Dépendance basée sur les annotations peuvent travailler ici, comme un code qui ne nécessite pas la connexion n'est pas et n'a pas besoin d'être fermée, mais il semble que votre conception est trop loin le long de rénovation de quelque chose comme ça.

Remplacer la méthode get() dans ThreaedLocal de sorte qu'il établit une Liste de propriété sur le sous-classe.Cette propriété peut facilement être interrogé pour déterminer si la méthode get() a été appelé pour un thread particulier.Vous pourriez alors avoir accès à la ThreadLocal pour le nettoyer dans ce cas.

Mis à jour en réponse à un commentaire

Ce que nous avons fait a été

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

Fondamentalement, il suffit de toujours (peut-être et alors) fermer pour tous les chemins à travers le fil.Dans le cas où il aide il ya quelqu'un :)

Je suis en train de regarder le même problème.Ressemble pour l'instant, vous devez utiliser finalize(), mais il est maintenant obsolète.À mesure que les tâches sont soumis à certaines Exécuteur testamentaire, vous n'auriez jamais savoir quand un thread exactement sorties, à moins que l'Exécuteur testamentaire vous montre, cela signifie que vous avez le contrôle de l'Exécuteur dans une certaine mesure.

Par exemple, si vous construisez un Exécuteur testamentaire, par l'extension ThreadPoolExecutor, vous pouvez ignorer la afterExecution() et la résiliation() les méthodes pour y parvenir.L'ancien, pour un fil de quitter anormalement tandis que le second pour normalement.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top