Czy wątek może się zablokować?

Czy technicznie możliwe jest zablokowanie wątku w Javie?

Zapytano mnie o to na wywiadzie jakiś czas temu I odpowiedział, że nie jest to możliwe, ale rozmówca powiedział mi, że tak jest. Niestety nie udało mi się uzyskać jego metody, jak osiągnąć ten impas.

To dało mi do myślenia i jedyną sytuacją, o której mogę myśleć, jest to, że możesz to zrobić, gdy masz proces serwera RMI, który zawierał metodę, która sama się wywołuje. The line of kod wywołujący metodę jest umieszczony w zsynchronizowanym bloku.

Czy to w ogóle możliwe, czy rozmówca się mylił?

Kod źródłowy, o którym myślałem, był podobny do tego (gdzie testDeadlock działa w procesie serwera RMI)

public boolean testDeadlock () throws RemoteException {
    synchronized (this) {
        //Call testDeadlock via RMI loopback            
    }
}
Author: Balder, 2010-08-16

18 answers

Na podstawie definicji:

Impas to sytuacja, w której dwie lub więcej akcji konkurencyjnych czeka na zakończenie drugiej.

Powiedziałbym, że odpowiedź brzmi nie-pewnie wątek może siedzieć i czekać na coś w nieskończoność, jednak jeśli dwie konkurujące ze sobą akcje czekają na siebie, to z definicji nie jest to impas.

Chyba, że ktoś mi wyjaśni jak pojedynczy wątek może jednocześnie czekać na dwa działania do zakończenia?

UPDATE: jedyną możliwą sytuacją, o której myślę, jest jakaś pompa komunikacyjna, gdzie wątek przetwarza wiadomość, która prosi ją o czekanie w nieskończoność nacoś , gdzie w rzeczywistości tocoś {8]} zostanie przetworzone przez inną wiadomość na pompie komunikacyjnej.

Ten (niewiarygodnie wymyślony) scenariusz można by technicznie nazwać impasem.

 47
Author: Justin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-16 13:38:55

To zależy od tego, co dokładnie rozumiesz przez "Impas". Na przykład, można łatwo wait() Na monitorze, który nigdy nie pulsuje... ale nie nazwałbym tego impasem.

Myśląc wzdłuż linii "metoda, która sama się wywołuje", jeśli twój serwer uruchomił tylko określoną liczbę wątków, wszystkie mogą być zajęte czekaniem na odpowiedzi z tego samego serwera, jeśli to się liczy. (Najprostszy przykład: serwer używa tylko jednego wątku do przetwarzania. Jeśli napiszesz zapytanie, które wywołania na ten sam serwer, będzie czekał na zablokowany wątek, aby zakończyć obsługę żądania, zanim będzie mógł obsłużyć to samo żądanie...) To nie jest tak naprawdę" synchronized block " rodzaj impasu, ale z pewnością jest to niebezpieczeństwo, aby być świadomym.

EDIT: aby zastosować tę odpowiedź do definicji w innych, konkurencyjne działania tutaj to "Zakończ bieżące żądanie" i "obsłuż nowe żądanie". Każda akcja czeka na drugą.

 17
Author: Jon Skeet,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-16 13:27:56

Może miał na myśli samą blokadę , to na pewno za proste:

synchronized( this )
{
    wait( );
}
 10
Author: Alexander Pogrebnyak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-16 13:19:56

Może to, o czym rozmówca myślał, było:

Thread.currentThread().join();

Jednak twierdzę, że nie liczy się jako impas.

 7
Author: finnw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-16 13:49:30

Aktualizacja z blokady odczytu do blokady zapisu (próba uzyskania blokady zapisu podczas trzymania blokady odczytu) spowoduje całkowite zablokowanie wątku. Czy to impas? Ty będziesz sędzią... Ale to najprostszy sposób na stworzenie efektu za pomocą jednego wątku.

Http://download.oracle.com/javase/6/docs/api/java/util/concurrent/locks/ReentrantReadWriteLock.html

 6
Author: sjlee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-16 14:36:35

Według Wikipedii " impas to sytuacja, w której dwie lub więcej konkurencyjnych akcji czeka na zakończenie drugiej, a zatem żadna z nich nigdy nie kończy."

..."W informatyce Coffman deadlock odnosi się do określonego warunku, gdy dwa lub więcej procesów czeka na siebie, aby uwolnić Zasób, lub więcej niż dwa procesy czekają na zasoby w okrągłym łańcuchu."

Myślę, że dwa lub więcej są kluczowymi słowami tutaj, jeśli pozostajesz Surowy do definicja.

 5
Author: guiding5,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-27 12:16:24

JVM tylko śledzi lokalny wątek, który ma monitor, jeśli Klasa wywołująca wykonuje zewnętrzne połączenie, wywołanie przychodzące powoduje zablokowanie oryginalnego wątku.

Powinieneś być w stanie uruchomić ten kod, aby zilustrować pomysł

import java.rmi.*;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.*;

public class DeadlockThreadExample {

    public static interface DeadlockClass extends Remote {
        public void execute() throws RemoteException;
    }

    public static class DeadlockClassImpl extends UnicastRemoteObject implements DeadlockClass {
        private Object lock = new Object();

        public DeadlockClassImpl() throws RemoteException {
            super();
        }

        public void execute() throws RemoteException {
            try {
                System.out.println("execute()::start");

                synchronized (lock) {
                    System.out.println("execute()::Entered Lock");
                    DeadlockClass deadlockClass = (DeadlockClass) Naming.lookup("rmi://localhost/DeadlockClass");
                    deadlockClass.execute();
                }
                System.out.println("execute()::Exited Lock");
            } catch (NotBoundException e) {
                System.out.println(e.getMessage());
            } catch (java.net.MalformedURLException e) {
                System.out.println(e.getMessage());
            }
            System.out.println("execute()::end");
        }
    }

    public static void main(String[] args) throws Exception {
        LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
        DeadlockClassImpl deadlockClassImpl = new DeadlockClassImpl();
        Naming.rebind("DeadlockClass", deadlockClassImpl);
        DeadlockClass deadlockClass = (DeadlockClass) Naming.lookup("rmi://localhost/DeadlockClass");
        deadlockClass.execute();
        System.exit(0);
    }
}

Wyjście z programu wygląda jak

execute()::start
execute()::Entered Lock
execute()::start

Dodatkowo wątek również dump pokazuje następujące

"main" prio=6 tid=0x00037fb8 nid=0xb80 runnable [0x0007f000..0x0007fc3c]
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.read(SocketInputStream.java:129)
    at java.io.BufferedInputStream.fill(BufferedInputStream.java:218)
    at java.io.BufferedInputStream.read(BufferedInputStream.java:235)
    - locked <0x02fdc568> (a java.io.BufferedInputStream)
    at java.io.DataInputStream.readByte(DataInputStream.java:241)


"RMI TCP Connection(4)-172.17.23.165" daemon prio=6 tid=0x0ad83d30 nid=0x1590 waiting for monitor entry [0x0b3cf000..0x0b3cfce8]
    at DeadlockThreadExample$DeadlockClassImpl.execute(DeadlockThreadExample.java:24)
    - waiting to lock <0x0300a848> (a java.lang.Object)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)


"RMI TCP Connection(2)-172.17.23.165" daemon prio=6 tid=0x0ad74008 nid=0x15f0 runnable [0x0b24f000..0x0b24fbe8] 
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.read(SocketInputStream.java:129)
    at java.io.BufferedInputStream.fill(BufferedInputStream.java:218)
    at java.io.BufferedInputStream.read(BufferedInputStream.java:235)
    - locked <0x02ffb6d8> (a java.io.BufferedInputStream)
    at java.io.DataInputStream.readByte(DataInputStream.java:241)

Co wskazuje, że wątek rzeczywiście zdołał się zablokować

 4
Author: Pram,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-17 12:20:52

Chociaż nie używałem Javy, to wcześniej zablokowałem aplikację z jednym wątkiem. IIRC: Routine a zablokował fragment danych, aby go zaktualizować. Rutyna B zablokowała również ten sam fragment danych, aby go zaktualizować. Ze względu na zmiany wymagań A skończyło się na B. Oops.

Oczywiście był to zwykły błąd programistyczny, który złapałem za pierwszym razem, gdy próbowałem uruchomić kod, ale sam się zablokował. Myślę, że tego typu blokady byłyby możliwe w każdym języku, który obsługuje system plików.

 2
Author: Loren Pechtel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-16 13:52:15

Odpowiedź (Pram ' s) oznaczona jako poprawna nie jest technicznie impasem, jak sugerowali inni. Jest zablokowany.

Sugerowałbym w Javie oparcie się na definicji Javy (która jest zgodna z ideą dwóch wątków). Ostatecznym sędzią może być JVM, Jeśli wykryje sam impas . Tak więc, w przykładzie Prama, wątek pokazałby coś takiego jak poniżej, jeśli byłby to genialny impas.

Deadlock detected
=================

"Negotiator-Thread-1":
  waiting to lock Monitor of com.google.code.tempusfugit.concurrency.DeadlockDetectorTest$Cat@ce4a8a
  which is held by "Kidnapper-Thread-0"

"Kidnapper-Thread-0":
  waiting to lock Monitor of com.google.code.tempusfugit.concurrency.DeadlockDetectorTest$Cash@7fc8b2
  which is held by "Negotiator-Thread-1"

To wykrywanie impasu było dostępne dla wewnętrznych blokady od 1.5 i Lock oparte na cyklicznych blokadach od 1.6.

Stale blokowany zasób, a przynajmniej coś, co czeka na coś, co nigdy się nie wydarzy, nazywa się livelock. Podobne problemy, gdzie procesy poza VM (na przykład) bazy danych deadlocking są całkowicie możliwe, ale uważam, że nie nadaje się do pytania.

Chciałbym napisać, jak rozmówca twierdzi, że to możliwe...

In answer to your original pytanie, do tanga trzeba dwojga i [[5]] sugerowałbym, aby odpowiedź Prama nie była oznaczana jako poprawna, ponieważ nie jest!;) wątek RMI, który oddzwania może spowodować blokowanie, ale działa na innym wątku (zarządzanym przez serwer RMI) niż w main. Dwa wątki są zaangażowane, nawet jeśli główny wątek nie wyraźnie skonfigurował innego. Nie ma impasu, o czym świadczy brak wykrywania w zrzucie wątku (lub jeśli klikniesz "Wykryj impas" w jconsole), byłoby dokładniej opisywany jako livelock.

Powiedziawszy to wszystko, każda dyskusja wzdłuż linii każdej z tych odpowiedzi byłaby wystarczająca, aby zadowolić mnie jako rozmówcę.

 2
Author: Toby,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-31 15:26:00

Nie, ponieważ Java implementuje reentrancy . Ale proszę, nie mieszaj w ten sposób współbieżności i RMI. Synchronizacja w stubach to coś zupełnie innego niż zdalne obiekty, które są wewnętrznie zsynchronizowane.

 1
Author: b_erb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-16 13:34:15

Możesz dostać się do jednego wątku impasu za pomocą ReentrantReadWriteLock . Blokady zapisu mogą uzyskać blokady odczytu, ale nie odwrotnie. Poniższe zostaną zablokowane na czas nieokreślony.

    ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
    lock.readLock().lock();
    lock.writeLock().lock();
 1
Author: mR_fr0g,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-08 15:45:45

Najlepiej, aby wątek nie tworzył samego impasu używając 'synchronized locks', chyba że w samym JVM jest błąd, który 'rzekomo' zauważył przez niektórych ludzi w starszych wersjach

 0
Author: Gopi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-16 13:22:52

Oto sposób, aby wątek się zablokował.

public class DeadlockMe
{
    public static void main(String[] args)
    {
        DeadlockThing foo = new DeadlockThing();
        synchronized(foo)
        {
            try
            {
                foo.wait();
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    }
}

Wątek tworzy instancję klasy-dowolnej klasy i czeka na nią. Ponieważ wątek utworzył obiekt z zakresem lokalnym, nie ma możliwości, aby jakikolwiek inny wątek powiadomił obiekt o jego obudzeniu.

 0
Author: JeremyP,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-16 14:51:58

Piszesz wątek, który może odbierać wiadomości z innych wątków, nakazując mu np. zakończenie. Piszesz kod w wątku, aby monitorować inne wątki i wysyłać do nich wiadomości i czekać na odpowiedź. Wątek znalazłby się na liście, wysłał sobie wiadomość do zakończenia i czekał na zakończenie. Jeśli nie został napisany w sposób, który priorytetował przychodzące wiadomości nad stanem oczekiwania ...

 0
Author: dwarFish,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-16 15:07:13

Jeśli rozciągniesz definicję terminu impas: pojedynczy wątek może znaleźć się zablokowany na nie-reentrantowym zamku, który zajął wcześniej.

 0
Author: carlsborg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-16 17:45:28

Gdy wątek wchodzi do zsynchronizowanego bloku, sprawdza, czy bieżący wątek jest właścicielem blokady, a jeśli tak, wątek po prostu przebiega bez czekania.

Więc nie wydaje mi się to możliwe.

 0
Author: Denis Tulskiy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-16 18:44:36

Wiem, że to stary post. Oto kolejny przykład, jak może się to stać, jeśli twój kod ma interakcję z zewnętrznymi zasobami:

Mam wątek, który otwiera połączenie z bazą danych, rozpoczyna transakcję i rozpoczyna aktualizację. Ten sam wątek, otwórz inne połączenie, rozpocznij kolejną transakcjęb. Ponieważ jednak transakcja nie została jeszcze zatwierdzona i ma zablokowaną tabelę bazy danych, transakcjab uzyskuje dostęp do tej zablokowanej tabeli, więc musi poczekać

W końcu ten sam wątek jest Zablokuj sam, ponieważ otworzyło więcej niż jedno połączenie z bazą danych.


Zdarzyło się to często w aplikacji, z którą pracuję, ponieważ w aplikacji jest wiele modułów, a wątek może działać za pomocą wielu metod. Metody te otwierają własne połączenia. Ponieważ mieliśmy różnych programistów pisać swój kod, mogą nie zobaczyć, jak ich Kod są zaczynać wywoływane, a zatem nie może zobaczyć wszystkich transakcji bazy danych, które otwarte przez aplikację.

 0
Author: dsum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-31 16:57:28

Rozmówca miał rację. wątek może się zablokować zgodnie z JCIP . Ale jak?

W sekcji 2.3.2 JCIP mamy następujący akapit o Reentrancy:

Reentrancy ułatwia hermetyzację zachowania blokady, a tym samym upraszcza rozwój obiektów kod współbieżności. Bez reentrantlocków, bardzo naturalnie wyglądający kod w listingu 2.7, w którym podklasa nadpisuje a zsynchronizowaną metodę, a następnie wywołuje super metoda klasowa.

Zsynchronizowana blokada słowa kluczowego jest blokadą reentrantową, więc wątek może blokować i odblokowywać w sposób zagnieżdżony, ale jeśli używasz blokady nie-reentrantowej, jak Poniższy przykład napisałem jako dowód. Będziesz miał impas! Według JCIP.

public class SelfDeadLock {


    public static class Father{
        volatile protected int n = 0;
        protected Lock ourLock = new Lock();

        public void writeSth(){
            try {
                ourLock.lock();
                n++;
                System.out.println("Father class: " + n);
            } catch (InterruptedException ex) {
                Logger.getLogger(SelfDeadLock.class.getName()).log(Level.SEVERE, null, ex);
            }
            ourLock.unlock();
        }
    }

    public static class Child extends Father{

        @Override
        public void writeSth() {
            try {
                ourLock.lock();
                n++;
                System.out.println("Child class: " + n);
                super.writeSth();
            } catch (InterruptedException ex) {
                Logger.getLogger(SelfDeadLock.class.getName()).log(Level.SEVERE, null, ex);
            }
            ourLock.unlock();
        }   
    }

    public static void main(String[] args) {
        Child child = new Child();
        child.writeSth();
    }
}
 0
Author: Johnny,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-12-29 03:12:45