Jaki jest najczęściej spotykany problem z współbieżnością w Javie? [zamknięte]

Obecnie pytanie to nie pasuje do naszego formatu pytań i odpowiedzi. Oczekujemy, że odpowiedzi będą poparte faktami, referencjami lub wiedzą specjalistyczną, ale to pytanie będzie prawdopodobnie wywoływało debatę, argumenty, ankiety lub rozszerzoną dyskusję. Jeśli uważasz, że to pytanie można poprawić i ewentualnie ponownie otworzyć, odwiedź Pomoc centrum dla wskazówek. Zamknięty 9 lat temu .

Jest to ankieta o typowych problemach współbieżności w Javie. Przykładem może być klasyczny impas lub stan wyścigu lub być może EDT gwintowanie błędów w huśtawce. Interesuje mnie zarówno szeroki zakres możliwych problemów, ale także to, jakie problemy są najczęściej spotykane. Więc proszę zostawić jedną konkretną odpowiedź na błąd współbieżności Javy za komentarz i zagłosuj, jeśli zobaczysz taki, który napotkałeś.

Author: Alex Miller, 2009-01-20

30 answers

Najczęstszym problemem współbieżności, jaki widziałem, jest nie uświadomienie sobie, że pole napisane przez jeden wątek jestnie gwarantowane być postrzegane przez inny wątek. Powszechne zastosowanie tego:

class MyThread extends Thread {
  private boolean stop = false;

  public void run() {
    while(!stop) {
      doSomeWork();
    }
  }

  public void setStop() {
    this.stop = true;
  }
}

Dopóki stop nie jestLotny lub setStop i run nie sązsynchronizowane to nie jest gwarantowane działanie. Ten błąd jest szczególnie diabelski, ponieważ w 99,999% nie będzie to miało znaczenia w praktyce, ponieważ wątek czytelnika w końcu zobaczy zmianę - ale nie wiemy, jak szybko widział to.

 126
Author: Kutzi,
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-07-18 16:24:29

Mój #1 najbardziej bolesny problem współbieżności kiedykolwiek wystąpił, gdy dwie różne biblioteki open source zrobiły coś takiego:

private static final String LOCK = "LOCK";  // use matching strings 
                                            // in two different libraries

public doSomestuff() {
   synchronized(LOCK) {
       this.work();
   }
}

Na pierwszy rzut oka wygląda to na dość trywialny przykład synchronizacji. Jednakże, ponieważ w Javie ciągi znaków są internowane, literalny ciąg znaków "LOCK" okazuje się być tą samą instancją java.lang.String (nawet jeśli są deklarowane całkowicie odmiennie od siebie.) Wynik jest oczywiście zły.

 179
Author: Jared,
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
2018-06-14 02:03:14

Jednym z klasycznych problemów jest zmiana obiektu, na którym synchronizujesz podczas synchronizacji na nim:

synchronized(foo) {
  foo = ...
}

Inne współbieżne wątki są następnie synchronizowane na innym obiekcie i blok ten nie zapewnia wzajemnego wykluczenia, którego oczekujesz.

 65
Author: Alex Miller,
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
2009-01-20 16:02:25

Częstym problemem jest używanie klas takich jak Calendar i SimpleDateFormat z wielu wątków (często przez buforowanie ich w zmiennej statycznej) bez synchronizacji. Klasy te nie są bezpieczne dla wątków, więc dostęp wielowątkowy ostatecznie spowoduje dziwne problemy z niespójnym stanem.

 50
Author: Alex Miller,
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
2009-01-20 16:17:36

Nieprawidłowa synchronizacja na obiektach zwracanych przez Collections.synchronizedXXX(), szczególnie podczas iteracji lub wielu operacji:

Map<String, String> map = Collections.synchronizedMap(new HashMap<String, String>());

...

if(!map.containsKey("foo"))
    map.put("foo", "bar");

To źle . Pomimo pojedynczych operacji synchronized, Stan mapy pomiędzy wywołaniem contains i put może zostać zmieniony przez inny wątek. Powinno być:

synchronized(map) {
    if(!map.containsKey("foo"))
        map.put("foo", "bar");
}

Lub z ConcurrentMap implementacja:

map.putIfAbsent("foo", "bar");
 48
Author: Dave Ray,
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
2018-06-14 01:55:40

Zamek Podwójnie Sprawdzony. W zasadzie.

Paradygmat, który zacząłem uczyć się problemów, kiedy pracowałem w BEA, jest taki, że ludzie będą sprawdzać singleton w następujący sposób:

public Class MySingleton {
  private static MySingleton s_instance;
  public static MySingleton getInstance() {
    if(s_instance == null) {
      synchronized(MySingleton.class) { s_instance = new MySingleton(); }
    }
    return s_instance;
  }
}

To nigdy nie działa, ponieważ inny wątek mógł dostać się do zsynchronizowanego bloku i s_instance nie jest już null. Więc naturalną zmianą jest wtedy:

  public static MySingleton getInstance() {
    if(s_instance == null) {
      synchronized(MySingleton.class) {
        if(s_instance == null) s_instance = new MySingleton();
      }
    }
    return s_instance;
  }

To też nie działa, ponieważ model pamięci Java go nie obsługuje. Musisz zadeklarować s_instance jako volatile to make it work, and even then it only work on Java 5.

Ludzie, którzy nie są zaznajomieni z zawiłościami modelu pamięci Java, cały czas to psują .

 47
Author: Kirk Wylie,
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
2009-01-20 17:06:52

Chociaż prawdopodobnie nie do końca o to prosisz, najczęstszym problemem związanym z współbieżnością, z którym się spotkałem (prawdopodobnie dlatego, że pojawia się w normalnym kodzie jednowątkowym) jest

java.util.ConcurrentModificationException

Spowodowane takimi rzeczami jak:

List<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c"));
for (String string : list) { list.remove(string); }
 37
Author: Fabian Steeg,
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
2009-01-20 16:14:23

Łatwo jest myśleć, że zsynchronizowane Kolekcje zapewniają większą ochronę niż w rzeczywistości i zapominają trzymać blokadę między połączeniami. Widziałem ten błąd kilka razy:

 List<String> l = Collections.synchronizedList(new ArrayList<String>());
 String[] s = l.toArray(new String[l.size()]);

Na przykład, w drugim wierszu powyżej, metody toArray() i size() są same w sobie bezpieczne dla wątku, ale {[2] } jest oceniana oddzielnie od toArray(), a blokada na liście nie jest utrzymywana pomiędzy tymi dwoma wywołaniami.

Jeśli uruchomisz ten kod z innym wątkiem jednocześnie usuwając elementy z listy, prędzej czy później otrzymasz nowy String[], który jest większy niż wymagany do przechowywania wszystkich elementów na liście i ma wartości null w ogonie. Łatwo jest myśleć, że ponieważ dwa wywołania metody do listy występują w jednej linii kodu, jest to w jakiś sposób operacja atomowa, ale tak nie jest.

 30
Author: 3 revs, 2 users 83%Nick,
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
2018-06-14 02:08:03

Najczęstszym błędem, który widzimy w moim miejscu pracy, jest to, że programiści wykonują długie operacje, takie jak wywołania serwera, na EDT, blokując GUI na kilka sekund i sprawiając, że aplikacja nie reaguje.

 29
Author: Eric Burke,
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
2009-01-20 17:01:15

Forgetting to wait () (or Condition.wait ()) w pętli, sprawdzając, czy warunek oczekiwania jest rzeczywiście prawdziwy. Bez tego natkniesz się na błędy z fałszywych przebudzeń wait (). Użycie kanoniczne powinno być:

 synchronized (obj) {
     while (<condition does not hold>) {
         obj.wait();
     }
     // do stuff based on condition being true
 }
 28
Author: Alex Miller,
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
2009-01-20 16:25:55

Innym powszechnym błędem jest słaba obsługa wyjątków. Gdy wątek tła rzuca wyjątek, jeśli nie obsłużysz go prawidłowo, możesz w ogóle nie zobaczyć śladu stosu. A może twoje zadanie w tle przestaje działać i nigdy nie zaczyna się ponownie, ponieważ nie udało ci się obsłużyć wyjątku.

 26
Author: Eric Burke,
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
2009-01-20 17:06:30

Dopóki nie poszedłem na zajęcia z Brianem Goetzem, nie zdawałem sobie sprawy, że niezsynchronizowane getter prywatnego pola zmutowanego przez zsynchronizowane setter jestnigdy nie gwarantuje zwracania zaktualizowanej wartości. Tylko wtedy, gdy zmienna jest chroniona przez zsynchronizowany blok na zarówno odczyt, jak i zapis, otrzymasz gwarancję ostatniej wartości zmiennej.

public class SomeClass{
    private Integer thing = 1;

    public synchronized void setThing(Integer thing)
        this.thing = thing;
    }

    /**
     * This may return 1 forever and ever no matter what is set
     * because the read is not synched
     */
    public Integer getThing(){
        return thing;  
    }
}
 22
Author: John Russell,
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
2018-06-14 01:59:11

Myśląc, że piszesz kod jednowątkowy, ale używając mutowalnych statyk (w tym singletonów). Oczywiście będą one dzielone między wątki. Zdarza się to zaskakująco często.

 15
Author: Tom Hawtin - tackline,
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
2009-01-20 16:45:15

Arbitralne wywołania metod nie powinny być wykonywane z zsynchronizowanych bloków.

Dave Ray poruszył to w swojej pierwszej odpowiedzi, a w rzeczywistości napotkałem również impas związany z wywoływaniem metod na słuchaczach z poziomu zsynchronizowanej metody. Myślę, że bardziej ogólną lekcją jest to, że wywołania metody nie powinny być wykonywane "w dziczy" z zsynchronizowanego bloku - nie masz pojęcia, czy wywołanie będzie długotrwałe, spowoduje impas, czy nieważne.

W tym przypadku, i zwykle ogólnie, rozwiązaniem było zmniejszenie zakresu zsynchronizowanego bloku, aby chronić krytycznąprywatną sekcję kodu.

Ponadto, ponieważ mieliśmy teraz dostęp do kolekcji słuchaczy poza zsynchronizowanym blokiem, zmieniliśmy ją na kolekcję kopiowania przy zapisie. Albo mogliśmy po prostu zrobić obronną kopię kolekcji. Chodzi o to, że zazwyczaj istnieją alternatywy, aby bezpiecznie uzyskać dostęp do kolekcji nieznanych obiektów.

 15
Author: Scott Bale,
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
2009-01-20 19:12:25

Ostatnim błędem związanym z współbieżnością, na który natrafiłem, był obiekt, który w swoim konstruktorze stworzył ExecutorService, ale gdy obiekt nie był już odwoływany, nigdy nie wyłączył ExecutorService. Tak więc, w ciągu kilku tygodni, tysiące wątków wyciekło, ostatecznie powodując awarię systemu. (Technicznie rzecz biorąc, nie rozbił się, ale przestał działać prawidłowo, kontynuując bieg.)

Technicznie, przypuszczam, że to nie jest problem współbieżności, ale problem związany z używaniem Javy.util.biblioteki współbieżne.

 13
Author: Eddie,
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
2009-01-22 05:25:47

Niezrównoważona synchronizacja, szczególnie na mapach wydaje się być dość powszechnym problemem. Wiele osób uważa, że synchronizacja na puts do mapy (nie ConcurrentMap, ale powiedzmy HashMap) i nie synchronizacja na gets jest wystarczająca. Może to jednak prowadzić do nieskończonej pętli podczas ponownego hashowania.

Ten sam problem (częściowa synchronizacja)może wystąpić wszędzie tam, gdzie współdzielisz stan z odczytem i zapisem.

 11
Author: Alex Miller,
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
2009-01-20 16:22:53

Napotkałem problem współbieżności z Serwletami, gdy istnieją zmienne pola, które będą ustawiane przez każde żądanie. Ale istnieje tylko jedna instancja servleta dla wszystkich żądań, więc działało to idealnie w środowisku jednego użytkownika, ale gdy więcej niż jeden użytkownik zażądał, servleta nie było nieprzewidywalne wyniki.

public class MyServlet implements Servlet{
    private Object something;

    public void service(ServletRequest request, ServletResponse response)
        throws ServletException, IOException{
        this.something = request.getAttribute("something");
        doSomething();
    }

    private void doSomething(){
        this.something ...
    }
}
 11
Author: Ludwig Wensauer,
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
2009-01-20 17:27:25

Nie do końca błąd, ale najgorszym grzechem jest udostępnienie biblioteki, z której będą korzystać inni ludzie, ale nie określenie, które klasy / metody są bezpieczne dla wątku, a które muszą być wywoływane tylko z jednego wątku itp.

Więcej osób powinno korzystać z adnotacji dotyczących współbieżności (np. @ThreadSafe, @GuardedBy itp.) opisanych w książce Goetza.

 10
Author: Neil Bartlett,
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
2009-01-21 11:22:43

Moim największym problemem zawsze były blokady, szczególnie spowodowane przez słuchaczy, którzy strzelają z zamka. W takich przypadkach bardzo łatwo jest odwrócić blokadę między dwoma gwintami. W moim przypadku pomiędzy symulacją działającą w jednym wątku a wizualizacją symulacji działającej w wątku interfejsu użytkownika.

EDIT: przesunięto drugą część do osobnej odpowiedzi.

 9
Author: Dave Ray,
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
2009-01-20 16:11:44

Uruchamianie wątku wewnątrz konstruktora klasy jest problematyczne. Jeśli klasa jest rozszerzona, wątek może zostać uruchomiony przed wykonaniem konstruktora podklasy .

 9
Author: grayger,
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
2018-06-14 01:56:42

Zmienne klasy we współdzielonych strukturach danych

Thread1:
    Person p = new Person("John");
    sharedMap.put("Key", p);
    assert(p.getName().equals("John");  // sometimes passes, sometimes fails

Thread2:
    Person p = sharedMap.get("Key");
    p.setName("Alfonso");

Gdy tak się dzieje, kod jest znacznie bardziej złożony niż ten uproszczony przykład. Replikacja, znalezienie i naprawienie błędu jest trudne. Być może można by tego uniknąć, gdybyśmy mogli oznaczyć pewne klasy jako niezmienne, a pewne struktury danych jako posiadające tylko niezmienne obiekty.

 8
Author: Steve McLeod,
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
2009-01-20 18:05:26

Synchronizacja na łańcuchu znaków lub stałej zdefiniowanej przez łańcuch znaków jest (potencjalnie)problemem, ponieważ łańcuch znaków jest internowany i będzie współdzielony przez kogokolwiek innego w JVM za pomocą tego samego łańcucha znaków. Wiem, że ten problem pojawił się w serwerach aplikacji i innych scenariuszach "kontenerów".

Przykład:

private static final String SOMETHING = "foo";

synchronized(SOMETHING) {
   //
}

W tym przypadku każdy, kto używa ciągu "foo" do zablokowania, współdzieli tę samą blokadę.

 8
Author: Alex Miller,
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
2009-01-20 20:55:44

Wierzę, że w przyszłości głównym problemem Javy będzie (brak) gwarancji widoczności dla konstruktorów. Na przykład, jeśli utworzysz następującą klasę

class MyClass {
    public int a = 1;
}

A następnie po prostu przeczytaj właściwość MyClass a z innego wątku, MyClass.a może wynosić 0 LUB 1, w zależności od implementacji JavaVM i nastroju. Dziś szanse na to, że " a " będzie równe 1 są bardzo wysokie. Ale na przyszłych maszynach NUMA może być inaczej. Wielu ludzi nie zdaje sobie z tego sprawy i wierzy, że nie muszą przejmować się wielowątkowością podczas fazy inicjalizacji.

 8
Author: Tim Jansen,
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
2016-02-01 09:49:29

Najgłupszym błędem, który często popełniam, jest zapomnienie synchronizacji przed wywołaniem notify () lub wait() na obiekcie.

 7
Author: Dave Ray,
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
2009-01-20 16:12:13

Użycie lokalnego "new Object ()" jako mutex.

synchronized (new Object())
{
    System.out.println("sdfs");
}
To jest bezużyteczne.
 7
Author: Ludwig Wensauer,
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
2009-01-20 17:38:24

Innym powszechnym problemem "współbieżności" jest używanie zsynchronizowanego kodu, gdy nie jest to wcale konieczne. Na przykład nadal widzę programistów używających StringBuffer lub nawet java.util.Vector (jako zmienne lokalne metody).

 7
Author: Kutzi,
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
2009-01-20 19:27:25

Wiele obiektów, które są zabezpieczone blokadą, ale są powszechnie dostępne kolejno. Mamy do czynienia z kilkoma przypadkami, w których zamki są uzyskiwane za pomocą różnych kodów w różnych kolejnościach, co prowadzi do impasu.

 6
Author: Brendan Cashman,
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
2009-01-20 16:15:37

Nie zdając sobie sprawy, że this w klasie wewnętrznej nie jest this klasy zewnętrznej. Zazwyczaj w anonimowej klasie wewnętrznej implementującej Runnable. Głównym problemem jest to, że ponieważ synchronizacja jest częścią wszystkich Object s, nie ma skutecznego sprawdzania typu statycznego. Widziałem to co najmniej dwa razy na Usenecie, i pojawia się również w Brian Goetz ' z Java Concurrency w praktyce.

Zamknięcia BGGA nie cierpią z tego powodu, ponieważ nie ma {[0] } dla zamknięcia (this odwołuje się do klasy zewnętrznej). Jeśli używasz obiektów innych niż this jako zamków, to obejdzie ten problem i inne.

 5
Author: Tom Hawtin - tackline,
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
2009-01-20 16:53:09

Użycie obiektu globalnego, takiego jak zmienna statyczna do blokowania.

Prowadzi to do bardzo złych wyników z powodu sporów.

 3
Author: kohlerm,
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
2009-01-20 16:03:01

Honesly? Przed pojawieniem się java.util.concurrent, najczęstszym problemem, na który rutynowo natknąłem się, było to, co nazywam "thread-thrashing": aplikacje, które używają wątków do współbieżności, ale pojawiają się zbyt wiele z nich i kończą się thrashingiem.

 3
Author: Brian Clapper,
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
2009-01-20 16:11:51