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 ;-)
IE suxx ;-)
Przepływ sterowany danymi - A takie Java'owe coś ;-)
Potfór ;-) czyli generator z yield w Java'ie
Sztuczki tropiciela błędów - breakpoint na sterydach 2 ;-)
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ń