Nasza starsza aplikacja jest zablokowana w strasznym środowisku (dobra, będę wymieniać nazwy, to Tapestry 4), która zawiera śmieszną liczbę EventListeners
(~ 100 000) dla najprostszych operacje. Zgaduję, że to jest coś więcej niż to, co javax.swing.event.EventListenerList
miało kiedykolwiek obsłużyć, i w tym niefortunnym przypadku użycia powoduje to pewne nieprzyjemne problemy z wydajnością.Dlaczego nie wymieniono elementu EventListenerList? (Lub: jakie są pułapki w jego wymianie?)
Spędziłem kilka godzin bicia się w dość naiwny HashMap/ArrayList
opartych wymiany poniżej, i to znacznie szybciej, niemal w każdym calu:
Dodaj 50.000 słuchaczy:
EventListenerList
> 2 sekundy- ~ 3,5 milisekundy
Pożarowe wydarzenie dla 50 000 słuchaczy:
EventListenerList
0,3-0,5 msEventListenerMap
0,4-0,5 ms
Usunąć 50000 detektory (po jednym na raz):
EventListenerList
> 2 sekundyEventListenerMap
~ 280 milisekund
Wypalanie może być po prostu wolniejsze, ale modyfikacja jest niezwykle szybka. Trzeba przyznać, że sytuacja, w jakiej znalazły się te ramy, jest patologiczna, ale nadal wydaje się, że dawno temu można było zastąpić ją. Oczywiście występują problemy z publicznym interfejsem API (np., Ujawnia on swoją surową wewnętrzną macierz stanów), ale musi być w tym coś więcej. Może są przypadki wielowątkowe, gdzie EventListenerList
jest o wiele bezpieczniejsze lub bardziej wydajne?
public class EventListenerMap
{
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock readLock = lock.readLock();
private final Lock writeLock = lock.writeLock();
private Map<Class, List> llMap = new HashMap<Class, List>();
public <L extends EventListener> void add (Class<L> listenerClass, L listener)
{
try
{
writeLock.lock();
List<L> list = getListenerList(listenerClass);
if (list == null)
{
list = new ArrayList<L>();
llMap.put(listenerClass, list);
}
list.add(listener);
}
finally
{
writeLock.unlock();
}
}
public <L extends EventListener> void remove (Class<L> listenerClass, L listener)
{
try
{
writeLock.lock();
List<L> list = getListenerList(listenerClass);
if (list != null)
{
list.remove(listener);
}
}
finally
{
writeLock.unlock();
}
}
@SuppressWarnings("unchecked")
public <L extends EventListener> L[] getListeners (Class<L> listenerClass)
{
L[] copy = (L[]) Array.newInstance(listenerClass, 0);
try
{
readLock.lock();
List<L> list = getListenerList(listenerClass);
if (list != null)
{
copy = (L[]) list.toArray(copy);
}
}
finally
{
readLock.unlock();
}
return copy;
}
@SuppressWarnings("unchecked")
private <L extends EventListener> List<L> getListenerList (Class<L> listenerClass)
{
return (List<L>) llMap.get(listenerClass);
}
}
+1 szalony (iest) kwestia tego miesiąca, o Q (do najprostszych operacji) to każdy z etapów oddzielnego lub są przykuci, z drzewa lub semafora, jeśli tak, to kolejny szalony (iest) sugestia, użyj eventhandler i aby zbudować ścieżkę w postaci String w czasie wykonywania, to może nie jest wymagane trzymanie w pamięci mnóstwa metod, phaaa Podoba mi się ten pomysł, który pochodzi z twojego pytania, pozwól RPG żyć wiecznie (to ten sam zwariowany niekończący się kod) – mKorbel
Czy masz literówka we względnych czasach ostrzału? Informujesz, że twoja implementacja może być "tylko wolniejsza od włosów" o 0,4 _milliseconds_ vs. 0.3 _seconds_ dla 'EventListenerList'. –
Tak, to był literówka, dzięki! –