W moim programie używam wielowątkowości w Javie. Uruchomiłem wątek pomyślnie, ale kiedy używam Thread.wait()
, rzuca java.lang.IllegalMonitorStateException
. Jak mogę sprawić, by wątek czekał, aż zostanie powiadomiony?
W moim programie używam wielowątkowości w Javie. Uruchomiłem wątek pomyślnie, ale kiedy używam Thread.wait()
, rzuca java.lang.IllegalMonitorStateException
. Jak mogę sprawić, by wątek czekał, aż zostanie powiadomiony?
Odpowiedzi:
Aby pracować, musisz być w synchronized
bloku Object.wait()
.
Polecam również przyjrzenie się pakietom współbieżności zamiast pakietów wątków starej szkoły. Są bezpieczniejsze i łatwiejsze w obsłudze .
Miłego kodowania.
EDYTOWAĆ
Założyłem, że Object.wait()
jako wyjątek miałeś na myśli to, co dzieje się, gdy próbujesz uzyskać dostęp bez blokowania obiektów.
wait
jest zdefiniowany w programie Object
, a nie w nim Thread
. Monitor włączony Thread
jest trochę nieprzewidywalny.
Chociaż wszystkie obiekty Java mają monitory, generalnie lepiej jest mieć dedykowaną blokadę:
private final Object lock = new Object();
Możesz uzyskać nieco łatwiejszą do odczytania diagnostykę przy niewielkim koszcie pamięci (około 2 KB na proces), używając nazwanej klasy:
private static final class Lock { }
private final Object lock = new Lock();
Aby obiekt wait
lub notify
/ notifyAll
obiekt, musisz trzymać blokadę z synchronized
instrukcją. Będziesz także potrzebował while
pętli, aby sprawdzić stan wybudzania (znajdź dobry tekst na temat wątków, aby wyjaśnić, dlaczego).
synchronized (lock) {
while (!isWakeupNeeded()) {
lock.wait();
}
}
Powiadamiać:
synchronized (lock) {
makeWakeupNeeded();
lock.notifyAll();
}
Podczas wchodzenia w wielowątkowość warto poznać zarówno język Java, jak i java.util.concurrent.locks
blokady (i java.util.concurrent.atomic
). Ale używaj java.util.concurrent
struktur danych, kiedy tylko możesz.
wait
, tak, nigdy byś tego nie zrobił notify
. Jednak w dokumentacji API dla Object.wait
„Wątek zwalnia własność tego monitora”. Więc gdy jest w środku wait
, jest tak, jakby był poza otaczającymi synchronized
blokami (dla tego samego obiektu może być wiele synchronized
bloków na tym samym obiekcie).
Wiem, że ten wątek ma prawie 2 lata, ale nadal muszę go zamknąć, ponieważ przyszedłem również do tej sesji Q / A z tym samym problemem ...
Przeczytaj tę definicję nielegalnego wyjątku MonitorException raz po raz ...
IllegalMonitorException jest generowany w celu wskazania, że wątek próbował czekać na monitorze obiektu lub aby powiadomić inne wątki oczekujące na monitorze obiektu bez posiadania określonego monitora.
Ta linia wielokrotnie mówi, że IllegalMonitorException pojawia się, gdy wystąpi jedna z 2 sytuacji ...
1> czekaj na monitorze obiektu bez posiadania określonego monitora.
2> powiadomić inne wątki oczekujące na monitorze obiektu bez posiadania określonego monitora.
Niektórzy mogliby mieć odpowiedzi ... a kto nie, sprawdź 2 stwierdzenia ...
zsynchronizowany (obiekt)
object.wait ()
Jeśli oba obiekty są takie same ... wtedy nie może nadejść żaden nielegalny wyjątek MonitorException.
Teraz ponownie przeczytaj definicję IllegalMonitorException i nie zapomnisz o tym ponownie ...
Na podstawie twoich komentarzy wygląda na to, że robisz coś takiego:
Thread thread = new Thread(new Runnable(){
public void run() { // do stuff }});
thread.start();
...
thread.wait();
Są trzy problemy.
Jak powiedzieli inni, obj.wait()
można go wywołać tylko wtedy, gdy bieżący wątek przechowuje pierwotną blokadę / mutex dla obj
. Jeśli bieżący wątek nie utrzymuje blokady, pojawi się wyjątek, który widzisz.
thread.wait()
Połączenie nie robi tego, co wydaje się być spodziewałem się zrobić. W szczególności thread.wait()
nie powoduje, że nominowany wątek czeka. Raczej powoduje, że bieżący wątek czeka, aż inne wywołania wątku thread.notify()
lub thread.notifyAll()
.
W rzeczywistości nie ma bezpiecznego sposobu na zmuszenie Thread
instancji do wstrzymania, jeśli nie chce. (Najbliżej tego, co ma Java, jest Thread.suspend()
metoda przestarzała , ale ta metoda jest z natury niebezpieczna, jak wyjaśniono w Javadoc.)
Jeśli chcesz, aby nowo rozpoczęty Thread
pauzował, najlepszym sposobem na to jest utworzenie CountdownLatch
instancji i wywołanie wątku await()
na zatrzasku, aby wstrzymało się. Główny wątek wywołałby wówczas countDown()
zatrzask, aby umożliwić kontynuację wstrzymanego wątku.
Prostopadłe do poprzednich punktów, użycie Thread
obiektu jako blokady / muteksu może powodować problemy. Na przykład javadoc dla Thread::join
mówi:
Ta implementacja używa pętli
this.wait
wywołań uwarunkowanychthis.isAlive
. Gdy wątek się kończy,this.notifyAll
wywoływana jest metoda. Zaleca się, aby nie korzystać z aplikacjiwait
,notify
lubnotifyAll
naThread
instancjach.
Ponieważ nie opublikowałeś kodu, pracujemy po omacku. Jakie są szczegóły wyjątku?
Czy wywołujesz Thread.wait () z poziomu wątku, czy poza nim?
Pytam o to, ponieważ zgodnie z javadoc dla IllegalMonitorStateException jest to:
Zgłaszane, aby wskazać, że wątek próbował czekać na monitorze obiektu lub powiadamiać inne wątki oczekujące na monitorze obiektu bez posiadania określonego monitora.
Aby wyjaśnić tę odpowiedź, to wywołanie oczekiwania na wątek zgłasza również wyjątek IllegalMonitorStateException, mimo że jest wywoływany z zsynchronizowanego bloku:
private static final class Lock { }
private final Object lock = new Lock();
@Test
public void testRun() {
ThreadWorker worker = new ThreadWorker();
System.out.println ("Starting worker");
worker.start();
System.out.println ("Worker started - telling it to wait");
try {
synchronized (lock) {
worker.wait();
}
} catch (InterruptedException e1) {
String msg = "InterruptedException: [" + e1.getLocalizedMessage() + "]";
System.out.println (msg);
e1.printStackTrace();
System.out.flush();
}
System.out.println ("Worker done waiting, we're now waiting for it by joining");
try {
worker.join();
} catch (InterruptedException ex) { }
}
wait()
.
worker.wait()
linii? Wtedy powinieneś synchronizować się z pracownikiem, a nie z zamkiem.
Aby poradzić sobie z wyjątkiem IllegalMonitorStateException, należy sprawdzić, czy wszystkie wywołania metod wait, notification i notifyAll mają miejsce tylko wtedy, gdy wątek wywołujący jest właścicielem odpowiedniego monitora . Najprostszym rozwiązaniem jest umieszczenie tych wywołań w zsynchronizowanych blokach. Obiekt synchronizacji, który ma zostać wywołany w zsynchronizowanej instrukcji, to ten, którego monitor należy pobrać.
Oto prosty przykład zrozumienia pojęcia monitora
public class SimpleMonitorState {
public static void main(String args[]) throws InterruptedException {
SimpleMonitorState t = new SimpleMonitorState();
SimpleRunnable m = new SimpleRunnable(t);
Thread t1 = new Thread(m);
t1.start();
t.call();
}
public void call() throws InterruptedException {
synchronized (this) {
wait();
System.out.println("Single by Threads ");
}
}
}
class SimpleRunnable implements Runnable {
SimpleMonitorState t;
SimpleRunnable(SimpleMonitorState t) {
this.t = t;
}
@Override
public void run() {
try {
// Sleep
Thread.sleep(10000);
synchronized (this.t) {
this.t.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Wywołanie Thread.wait () ma sens w kodzie, który synchronizuje się z obiektem Thread.class. Nie sądzę, że to miałeś na myśli.
Ty pytasz
Jak mogę sprawić, by wątek czekał, aż zostanie powiadomiony?
Możesz sprawić, by czekał tylko bieżący wątek. Każdy inny wątek może zostać delikatnie poproszony o czekanie, jeśli się zgodzi.
Jeśli chcesz poczekać na jakiś warunek, potrzebujesz obiektu blokującego - obiekt Thread.class to bardzo zły wybór - jest to singleton AFAIK, więc synchronizacja na nim (poza metodami statycznymi Thread) jest niebezpieczna.
Szczegóły dotyczące synchronizacji i oczekiwania zostały już wyjaśnione przez Toma Hawtina.
java.lang.IllegalMonitorStateException
oznacza, że próbujesz czekać na obiekt, z którym nie jesteś zsynchronizowany - jest to nielegalne.
Nie jestem pewien, czy to pomoże komuś innemu, czy nie, ale to była kluczowa część rozwiązania mojego problemu w odpowiedzi użytkownika „Tom Hawtin - tacklin” powyżej:
synchronized (lock) {
makeWakeupNeeded();
lock.notifyAll();
}
Po prostu fakt, że "lock" jest przekazywany jako argument w synchronized () i jest również używany w "lock" .notifyAll ();
Kiedy już zrobiłem to w tych 2 miejscach, to działało
Przez IllegalMonitorStateException
chwilę próbowałem obudzić wątek w / z innego class
/ wątku. W java 8
można wykorzystać lock
możliwości nowego Współbieżnym API zamiast z synchronized
funkcji.
Przechowywałem już obiekty dla asynchronous
transakcji WebSocket w pliku WeakHashMap
. Rozwiązaniem w moim przypadku było również przechowywanie lock
obiektu w aConcurrentHashMap
dla synchronous
odpowiedzi. Zwróć uwagę na condition.await
(nie .wait
).
Aby obsłużyć wielowątkowość, użyłem a Executors.newCachedThreadPool()
do utworzenia puli wątków .
Ci, którzy używają Javy 7.0 lub niższej, mogą odwołać się do kodu, którego tutaj użyłem i działa.
public class WaitTest {
private final Lock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
public void waitHere(long waitTime) {
System.out.println("wait started...");
lock.lock();
try {
condition.await(waitTime, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
lock.unlock();
System.out.println("wait ends here...");
}
public static void main(String[] args) {
//Your Code
new WaitTest().waitHere(10);
//Your Code
}
}