7 Stimmen

Wie man einen gemeinsamen Kontext pro Top-Level-Prozess/Thread ohne Verwendung von InheritableThreadLocal haben?

Ich würde gerne sehen, ob es ein gutes Muster für die gemeinsame Nutzung eines Kontexts für alle Klassen und Subthreads eines Top-Level-Threads gibt, ohne InheritableThreadLocal .

Ich habe mehrere Top-Level-Prozesse, die jeweils in ihrem eigenen Thread laufen. Diese Top-Level-Prozesse erzeugen oft temporäre Subthreads.

Ich möchte, dass jeder Prozess der obersten Ebene seine eigene Datenbankverbindung hat und verwaltet.

Ich habe pas die Datenbankverbindung von Klasse zu Klasse und von Thread zu Subthread weitergeben wollen (mein Kollege nennt dies das "Community Bicycle"-Muster). Dies sind große Top-Level-Prozesse und es würde bedeuten, dass wahrscheinlich Hunderte von Methodensignaturen bearbeitet werden müssten, um diese Datenbankverbindung weiterzugeben.

Im Moment rufe ich ein Singleton auf, um den Datenbankverbindungsmanager zu erhalten. Das Singleton verwendet InheritableThreadLocal so dass jeder Top-Level-Prozess seine eigene Version davon hat. Ich weiß zwar, dass einige Leute Probleme mit Singletons haben, aber das bedeutet, dass ich einfach sagen kann DBConnector.getDBConnection(args) (um es mal so auszudrücken), wenn ich eine korrekt verwaltete Verbindung brauche. Ich bin nicht an diese Methode gebunden, wenn ich eine bessere und dennoch saubere Lösung finden kann.

Aus verschiedenen Gründen InheritableThreadLocal erweist sich als knifflig. (Siehe diese Frage .)

Hat jemand einen Vorschlag, wie man so etwas handhaben kann, ohne dass man entweder InheritableThreadLocal oder ein Kontextobjekt überall herumreichen?

Danke für jede Hilfe!


Update: Ich habe es geschafft, das unmittelbare Problem zu lösen (siehe die verlinkte Frage), aber ich würde immer noch gerne von anderen möglichen Ansätzen hören. Der Vorschlag von forty-two unten ist gut und funktioniert (danke!), aber siehe die Kommentare, warum er problematisch ist. Wenn die Leute für jtahlborns Antwort stimmen und mir sagen, dass ich zwanghaft bin, weil ich die Weitergabe meiner Datenbankverbindung vermeiden will, werde ich einlenken, diese Antwort wählen und meine Weltanschauung revidieren.

3voto

forty-two Punkte 12141

Ich habe dies nicht getestet, aber die Idee ist, einen angepassten ThreadPoolExecutor zu erstellen, der weiß, wie man das Kontextobjekt erhält und #beforeExecute() verwendet, um das Kontextobjekt an den Thread zu übertragen, der die Aufgabe ausführen wird. Um ein guter Bürger zu sein, sollten Sie das Kontextobjekt auch in #afterEXecute() löschen, aber das lasse ich als Übung stehen.

public class XyzThreadPoolExecutor extends ThreadPoolExecutor  {

public XyzThreadPoolExecutor() {
    super(3, 3, 100, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), new MyThreadFactory());
}

@Override
public void execute(Runnable command) {
    /*
     * get the context object from the calling thread
     */
    Object context = null;
    super.execute(new MyRunnable(context, command));
}

@Override
protected void beforeExecute(Thread t, Runnable r) {
    ((MyRunnable)r).updateThreadLocal((MyThread) t);
    super.beforeExecute(t, r);
}

private static class MyThreadFactory implements ThreadFactory {
    @Override
    public Thread newThread(Runnable r) {
        return new MyThread(r);
    }

}

private class MyRunnable implements Runnable {
    private final Object context;
    private final Runnable delegate;

    public MyRunnable(Object context, Runnable delegate) {
        super();
        this.context = context;
        this.delegate = delegate;
    }

    void updateThreadLocal(MyThread thread) {
        thread.setContext(context);
    }

    @Override
    public void run() {
        delegate.run();

    }
}

private static class MyThread extends Thread {

    public MyThread(Runnable target) {
        super(target);
    }

    public void setContext(Object context) {
        // set the context object here using thread local
    }

}
}

2voto

OldCurmudgeon Punkte 62510

Als ThreadLocal ist im Wesentlichen ein Map auf Ihr Thema anspielt, könnten Sie nicht eine Map auf Ihr Thema gestoßen Name ? Dann brauchen Sie nur noch eine wirksame Benennungsstrategie, die Ihren Anforderungen gerecht wird.

1voto

manocha_ak Punkte 904

Ich würde Ihre Weltanschauung und jthalborns Idee nicht unterstützen, weil sie sogar besser überprüfbar ist.

Ich umschreibe das, was ich aus Ihrer Problemstellung verstanden habe, folgendermaßen.

  1. Es gibt 3 oder 4 Top-Level-Prozesse (und sie haben im Grunde einen eigenen Thread). Und Verbindungsobjekt ist, was in ihnen diffrenet ist.
  2. Sie benötigen einige grundlegende Merkmale der Verbindung, die Sie einmalig einrichten müssen.
  3. Die untergeordneten Threads ändern in keiner Weise das Verbindungsobjekt, das ihnen von den übergeordneten Threads übergeben wurde.

Hier ist, was ich vorschlage, Sie brauchen die einmalige Einrichtung der Verbindung, aber dann in jedem Ihrer Top-Level-Prozess, Sie tun 1) weitere Verarbeitung dieser Verbindung 2) halten eine InheriatbleThreadLocal (und das Kind Prozess Ihrer Top-Level-Thread wird das geänderte Verbindungsobjekt haben. 3) Übergeben Sie diese drei implementierenden Klassen. MyThread1, MyThread2, MyThread3, ... MyThread4 an den Executor. (Dies unterscheidet sich von der anderen verlinkten Frage von Ihnen, dass, wenn Sie einige Gating benötigen, Semaphore ist ein besserer Ansatz)

Warum ich sagte, dass seine nicht weniger testbar als jthalborns Ansicht ist, dass in diesem Fall auch Sie sowieso wieder braucht, um Mocked Connection Objekt bereitzustellen. Auch hier. Außerdem ist die konsequente Übergabe des Objekts und die Aufbewahrung des Objekts in ThreadLocal ein und dasselbe (InheritableThreadLocal ist eine Map, die auf Java-eigene Weise übergeben wird, ich glaube, das ist nichts Schlimmes).

EDIT: Ich habe bedacht, dass es sich um ein geschlossenes System handelt und wir keine "freien" Threads haben, die mit der Verbindung verlockend sind

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X