Jednak gdy mowa o aplikacji która pracuje wielowątkowo [a takich jest coraz więcej] nie zawsze jest to podejście najlepsze.
Wtedy przydaje się logowanie.
A w logowaniu, szczególnie gdy tropimy błąd przydaje się taki oto banalny kawałek kodu:
Throwable tr = new Throwable();
tr.printStackTrace();
Niby nic, a cieszy ;-) Wiemy już kto wywołał nasz kawałek kodu.
Czasem przydaje się też wędrówka po stack trace'ie:
public static void logStackTrace() {
Throwable tr = new Throwable();
StackTraceElement[] elements = tr.getStackTrace();
String str = "";
// zaczynamy od 1 żeby nie było widać wołania tej metody ;-)
for (int idx=1; idx<elements.length; idx++) {
str+=elements[idx]+" ";
}
System.out.println(str);
}
Czasem potrzebujemy też wiedzieć jakie wątki odwołują się do naszego kawałka kodu, wtedy używamy równie znanego:
Thread.currentThread();
Możemy go wypisać:
System.out.println(Thread.currentThread();
Albo użyć jako klucza w mapie:
// Używamy Hashtable'a bo jest bezpieczniejsza w przypadku wielu wątków
Map<Thread,Throwable> map = new Hashtable<Thread,Throwable>();
[....]
map.put(Thread.currentThread(), new Throwable());
Wyobraźmy sobie sytuację, że otwieramy połączenie i zamykamy je, ale nie chcemy mając jedno otwarte połączenie z danego wątku otwierać drugiego i kolejnych. W wytropieniu takiego czegoś pomóc nam może:
// Używamy Hashtable'a bo jest bezpieczniejsza w przypadku wielu wątków
public static Map<Thread,Throwable> map = new Hashtable<Thread,Throwable>();
public void openSomething() {
Throwable tr = new Throwable();
// używamy get, bo nie ufam contains, choć tutaj by świetnie zadziałało ;-)
if (map.get(Thread.currentThread())!=null) {
System.out.println("PROBLEM!!!!!");
tr.printStackTrace();
}
map.put(Thread.currentThread(), new Throwable());
[...]
}
public void closeSomething() {
map.remove(Thread.currentThread());
[....]
}
Jak widać jest to wszystko proste, a na dodatek skuteczne.
Zwykle do logowania nie używamy też
System.out
a czegoś bardziej sexy ;-) ale to już inna bajka.Na koniec jeszcze przykład jak można logować w trakcie tropienia błędu nieobsłużone wyjątki:
Runnable r = new Runnable() {
@Override
public void run() {
while (true) {
Object o = null;
try {
o.wait();
} catch (InterruptedException ie) {
ie.printStackTrace();
}
}
}
};
Thread t = new Thread(r);
t.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println(t);
e.printStackTrace();
}
});
t.start();
Oczywiście takie ustrojstwa mają sens w kodzie w trakcie developmentu, a nie na produkcji, choć to zależy chyba tylko od naszego wyboru :-)
[edit 01/03/2008 11:34: zmieniłem HashMap'e w Hashtable'a, bo walnąłem babola ;-) To Hashtable jest bezpieczne wątkowo, nie HashMap'a]
Podobne postybeta
Sztuczki tropiciela błędów - breakpoint na sterydach ;-)
Potfór ;-) czyli generator z yield w Java'ie
Przepływ sterowany danymi - A takie Java'owe coś ;-)
wait() i notify()/notifyAll() - najbardziej nierozumiane metody klasy Object ;-)
IE suxx ;-)
Ciekawe, ciekawe... :) Dodam więc swoje 0.03f... Zamiast new Throwable()... można też użyć Thread.dumpStack() lub od wersji 5.0 Thread.getStackTrace(). Zawsze to mniej pisania ;) Czasami dobrze też wypisywać debugi poprzez System.err.*, żeby nie kolidowało z normalnymi wynikami pracy programu..
OdpowiedzUsuń"Używamy HashMap'y bo jest bezpieczniejsza w przypadku wielu wątków"
Przecież to Hashtable jest synchronizowane ?
Z tą HashMap'ą to rzeczywiście jest tak jak piszesz :-) HashMap'a jest niebezpieczna, a Hashtable'jest bezpieczna. Jedyne co mnie tłumaczy to późna godzina :-)
OdpowiedzUsuńJuż poprawiam :-)
Rzeczywiście te dumpStack() i getStackTrace() krótsze w zapisie :-)
I tak najlepsza bylaby ConcurrentHashMap-a ;-)
OdpowiedzUsuń