2016-04-16 14 views
4

Ich verwende eine benutzerdefinierte Klasse Foo in Java als Schlüsseltyp in HashMap. Alle Felder von Foo Instanzen sind unveränderlich (sie werden als final und private deklariert und ihnen werden nur Werte im Konstruktor zugewiesen). Somit ist das hashCode() eines gegebenen Foo Objekts ebenfalls fest, und zu Optimierungszwecken berechne ich es im Konstruktor und gebe diesen Wert einfach in der hashCode() Methode zurück.Java-Optimierung mit Threads

Instanzen von Foo haben auch eine value() Methode, die einen ähnlichen festen Wert zurückgibt, sobald das Objekt instanziiert wurde. Momentan berechne ich es auch im Konstruktor und gebe es in der Methode zurück, aber es gibt einen Unterschied zwischen hashCode() und value(): hashCode() wird zum ersten Mal fast sofort nach dem Erstellen des Objekts aufgerufen, aber value() wird viel später aufgerufen. Ich verstehe, dass einen separaten Thread mit dem Hash-Code berechnen würde einfach die Laufzeit erhöhen, weil die Synchronisierungsprobleme, aber:

  • ist dies ein guter Weg value() zu berechnen? Würde es die Laufzeit verbessern?
  • sind einfach Threads genug, oder brauche ich Pools usw.?

Hinweis: dies mag wie ich die falschen Teile meines Programms bin zu optimieren, aber ich habe gearbeitet bereits auf den ‚richtigen‘ Teile und brachte die durchschnittliche Laufzeit nach unten von ~ 17 Sekunden ~ 2 Sekunden. Edit: wird es mehr als 5000 Foo Objekte, und das ist eine vorsichtige Schätzung.

+0

in Bezug auf die Konzeption und Architektur wickeln, wäre es seltsam, als 'value()' der Lage wäre, um einen ungültigen Wert für eine Zeit zurückzugeben. – njzk2

+0

@ njzk2 Das ist gut für mein Programm; Wie ich bereits sagte, wird 'value()' lange nach der Erstellung des Objekts aufgerufen. – shardulc

Antwort

2

Es klingt definitiv wie aufgeschobene Berechnung ist hier ein guter Ansatz - und ja, wenn Sie eine Menge dieser Objekte erstellen, ist ein Thread-Pool der richtige Weg.

Wie für value() Rückgabewert, bis es fertig ist, würde ich bleiben weg von ungültigen Werte zurück, und stattdessen entweder blockieren (und fügen Sie einige isValueReady() Helfer) oder machen es sofort eine "Zukunft" - einige Objekt, das bietet die gleichen isReady und eine Blockierung get Methoden.

Verlassen Sie sich auch nie auf "viel später" - stellen Sie immer sicher, dass der Wert bereit ist, bevor Sie ihn verwenden.

+0

Theoretisch klingt deine Antwort richtig für mich, aber in der Praxis läuft etwas schief: Das Programm benötigt mehr als das 5-fache der üblichen Laufzeit. Welche Angaben sollte ich machen, um meine Situation genauer zu machen? – shardulc

+0

@shardulc Es gibt viele mögliche Variablen, und ich kenne Ihre Anwendung nicht. Ich würde zuerst zwei Dinge betrachten: 1. Wie schwer berechnet 'value()'? Wenn das Erstellen eines Thread Threads schwerer ist, wird es sich nie lohnen. 2. Wie CPU-hungrig ist Ihre Anwendung? Wenn es eine 100% ige CPU-Auslastung hat, hat die Verschiebung einer Berechnung keinen Vorteil und Sie zahlen immer noch den Overhead. Aber ehrlich gesagt, könnte es etwas ganz anderes sein ... Ich verlasse mich auf Profilerstellung, wenn es aus dem Code nicht offensichtlich ist oder aus dem Debugging, warum etwas langsam ist. – Oak

1

Ich empfehle eine Future für value Erstellung - eine statische fixedTheadPool und submit die value Berechnungen auf sie erstellen. Auf diese Weise besteht keine Gefahr, dass value zugegriffen wird, bevor es verfügbar ist - der schlimmste Fall ist, dass alles, was value zugreift auf einem Future.get Anruf sperren (oder use the version with a timeout wenn zB Deadlock ist ein Anliegen)

Da Future.get Würfen Ausnahmen geprüft, wo ein Ärgernis sein, können Sie den get Anruf in der Klasse der Getter-Methode wickeln und die geprüfte Ausnahmen in einem RuntimeException

class MyClass { 
    private static final ExecutorService executor = Executors.newFixedThreadPool(/* some value that makes sense */); 

    private final Future<Value> future; 

    public MyClass() { 
     future = executor.submit(/* Callable */); 
    } 

    public boolean isValueDone() { 
     return future.isDone(); 
    } 

    public Value value() { 
     try { 
      return future.get(); 
     } catch(InterruptedException|ExecutionException e) { 
      throw new RuntimeException(e); 
     } 
    } 
}