Jaki jest najczęstszy problem współbieżności, który napotkałeś w Javie? [Zamknięte]


191

To rodzaj ankiety na temat typowych problemów z współbieżnością w Javie. Przykładem może być klasyczny impas lub stan wyścigu, a może błędy EDT w Swing. Interesuje mnie zarówno szeroki zakres możliwych problemów, jak i te, które są najczęstsze. Więc zostaw jedną konkretną odpowiedź błędu współbieżności Java na komentarz i głosuj, jeśli zobaczysz napotkany błąd.


16
Dlaczego to jest zamknięte? Jest to przydatne zarówno dla innych programistów, którzy proszą o współbieżność w Javie, jak i pozwala zorientować się, jakie klasy defektów współbieżności są najczęściej obserwowane przez innych programistów Java.
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳

@Longpoke Komunikat zamknięcia wyjaśnia, dlaczego jest zamknięty. To nie jest pytanie z konkretną „poprawną” odpowiedzią, to raczej pytanie ankiety / listy. A przepełnienie stosu nie ma zamiaru obsługiwać tego rodzaju pytań. Jeśli nie zgadzasz się z tą polityką, możesz omówić ją na stronie meta .
Andrzej Doyle

7
Wydaje mi się, że społeczność się nie zgadza, ponieważ ten artykuł ma ponad 100 wyświetleń dziennie! Uznałem, że jest to bardzo przydatne, ponieważ jestem zaangażowany w opracowywanie narzędzia do analizy statycznej zaprojektowanego specjalnie do rozwiązywania problemów z współbieżnością contemplateltd.com/threadsafe . Posiadanie banku często spotykanych problemów z współbieżnością było świetne do testowania i ulepszania ThreadSafe.
Craig Manson

Lista kontrolna przeglądu kodu dla współbieżności Java podsumowuje większość pułapek wymienionych w odpowiedziach na to pytanie w formie dogodnej do codziennych przeglądów kodu.
leventov

Odpowiedzi:


125

Najczęstszym problemem współbieżności, jaki widziałem, jest niezrozumienie, że pole zapisane przez jeden wątek nie jest gwarantowane przez inny wątek. Typowe zastosowanie tego:

class MyThread extends Thread {
  private boolean stop = false;

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

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

Dopóki przystanku nie jest lotny albo setStopi runnie są zsynchronizowane to nie jest gwarantowane do pracy. Ten błąd jest szczególnie diabelski, ponieważ w 99,999% w praktyce nie będzie to miało znaczenia, ponieważ wątek czytelnika w końcu zobaczy zmianę - ale nie wiemy, jak szybko ją zobaczył.


9
Świetnym rozwiązaniem tego problemu jest ustawienie zmiennej instancji stop na AtomicBoolean. Rozwiązuje wszystkie problemy związane z nieulotnością, jednocześnie chroniąc Cię przed problemami JMM.
Kirk Wylie

39
Jest gorzej niż „przez kilka minut” - NIGDY nie możesz tego zobaczyć. Zgodnie z modelem pamięci JVM może optymalizować, dopóki (! Stop) do while (prawda), a następnie jesteś zablokowany. Może się to zdarzyć tylko na niektórych maszynach wirtualnych, tylko w trybie serwera, tylko wtedy, gdy JVM rekompiluje po x iteracjach pętli itp. Ojej!
Cowan

2
Dlaczego miałbyś chcieć używać AtomicBoolean zamiast lotnych wartości logicznych? Zajmuję się tworzeniem wersji 1.4+, więc czy są jakieś pułapki związane z deklarowaniem niestabilności?
Pula

2
Nick, myślę, że to dlatego, że atomowy CAS jest zwykle nawet szybszy niż lotny. Jeśli rozwija 1.4 swojej IMHO tylko bezpiecznym rozwiązaniem jest użycie zsynchronizowane jako lotny w 1.4 nie dostał mocne gwarancje bariera pamięci, jak to ma w Javie 5.
kutzi

5
@ Thomas: wynika to z modelu pamięci Java. Powinieneś przeczytać o tym, jeśli chcesz go szczegółowo poznać (Java Concurrency in Practice autorstwa Briana Goetza wyjaśnia to dobrze np.). W skrócie: chyba, że ​​użyjesz słów kluczowych / konstrukcji synchronizujących pamięć (takich jak lotne, zsynchronizowane, AtomicXyz, ale także po zakończeniu wątku), jeden wątek NIE ma żadnej gwarancji, że zobaczy zmiany wprowadzone w dowolnym polu wykonane przez inny wątek
Kutzi

178

Mój najbardziej bolesny problem z współbieżnością miał miejsce, 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; ponieważ ciągi są internowane w Javie, dosłowny ciąg "LOCK"okazuje się być tym samym wystąpieniem java.lang.String(nawet jeśli są one zadeklarowane całkowicie odmiennie od siebie). Wynik jest oczywiście zły.


62
Jest to jeden z powodów, dla których wolę prywatny statyczny końcowy obiekt LOCK = new Object ();
Andrzej Doyle

17
Uwielbiam to - och, to jest okropne :)
Thorbjørn Ravn Andersen

7
To jest dobre dla Java Puzzlers 2.
Dov Wasserman

12
Właściwie ... to naprawdę sprawia, że ​​chcę, aby kompilator odmówił zezwolenia na synchronizację na łańcuchu. Biorąc pod uwagę internowanie String, nie ma przypadku, w którym byłoby to „dobrą rzeczą (tm)”.
Jared

3
@Jared: „dopóki łańcuch nie zostanie internowany” nie ma sensu. Łańcuchy nie magicznie „zostają” internowane. String.intern () zwraca inny obiekt, chyba że masz już instancję kanoniczną określonego ciągu. Ponadto wszystkie łańcuchy literalne i wyrażenia stałe o wartościach łańcuchowych są internowane. Zawsze. Zobacz dokumentację dotyczącą String.intern () i §3.10.5 JLS.
Laurence Gonsalves,

65

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

synchronized(foo) {
  foo = ...
}

Inne współbieżne wątki synchronizują się następnie na innym obiekcie, a ten blok nie zapewnia oczekiwanego wzajemnego wykluczenia.


19
Istnieje inspekcja IDEA pod nazwą „Synchronizacja w polu niekończącym, która prawdopodobnie nie będzie miała użytecznej semantyki”. Bardzo dobrze.
Jen S.

8
Ha ... teraz to torturowany opis. „mało prawdopodobne, aby miał użyteczne semantyki” można lepiej opisać jako „najprawdopodobniej zepsute”. :)
Alex Miller

Myślę, że to Bitter Java miał to w ReadWriteLock. Na szczęście mamy teraz java.util.concurrency.locks, a Doug jest trochę bardziej aktywny.
Tom Hawtin - tackline

Często też widziałem ten problem. W tym przypadku synchronizuj tylko obiekty końcowe. FindBugs i in. pomoc, tak.
gimpf

czy to tylko problem podczas przydzielania? (patrz przykład @Alex Miller poniżej z mapą) Czy ten przykład mapy miałby również ten sam problem?
Alex Beardsley

50

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.


Czy znasz jakiś projekt typu open source zawierający ten błąd w jakiejś jego wersji? Szukam konkretnych przykładów tego błędu w prawdziwym oprogramowaniu.
przeprogramowanie

47

Blokowanie z podwójną kontrolą. Ogólnie mówiąc.

Paradygmat, o którym zacząłem uczyć się problemów podczas pracy w BEA, polega na tym, że ludzie sprawdzą singletona 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, a s_instance nie jest już pusty. Zatem naturalną zmianą jest to, aby:

  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 nie obsługuje tego. Musisz zadeklarować s_instance jako niestabilny, aby działał, a nawet wtedy działa tylko na Javie 5.

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


7
Wzór pojedynczego enum rozwiązuje wszystkie te problemy (patrz komentarze Josha Blocha na ten temat). Wiedza o jego istnieniu powinna być bardziej rozpowszechniona wśród programistów Java.
Robin

Nadal muszę natknąć się na jeden przypadek, w którym leniwa inicjalizacja singletona była właściwie odpowiednia. A jeśli tak, to po prostu zadeklaruj metodę zsynchronizowaną.
Dov Wasserman

3
Tego właśnie używam do leniwej inicjalizacji klas Singleton. Również nie jest wymagana synchronizacja, ponieważ Java gwarantuje to domyślnie. class Foo {static class Holder {static Foo foo = new Foo (); } static Foo getInstance () {return Holder.foo; }}
Irfan Zulfiqar

Irfan, to się nazywa metoda Pugha, z tego co pamiętam
Chris R

@ Robin, czy nie jest prostsze użycie statycznego inicjatora? Zawsze gwarantuje się synchronizację.
matt b

47

Niepoprawna synchronizacja na obiektach zwróconych 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 między wywoływaniem containsi putmoże być zmieniony przez inny wątek. Powinno być:

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

Lub z ConcurrentMapimplementacją:

map.putIfAbsent("foo", "bar");

5
Lub lepiej, użyj ConcurrentHashMap i putIfAbsent.
Tom Hawtin - tackline

37

Chociaż prawdopodobnie nie dokładnie to, o co prosisz, najczęstszym problemem związanym z współbieżnością, jaki napotkałem (prawdopodobnie dlatego, że pojawia się w normalnym kodzie jednowątkowym), jest

java.util.ConcurrentModificationException

spowodowane przez takie rzeczy jak:

List<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c"));
for (String string : list) { list.remove(string); }

Nie, tego właśnie szukam. Dzięki!
Alex Miller,

30

Łatwo jest pomyśleć, że zsynchronizowane kolekcje zapewniają większą ochronę niż w rzeczywistości, i zapominają trzymać blokadę między rozmowami. Kilka razy widziałem ten błąd:

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

Na przykład w drugim wierszu powyżej obie metody toArray()i size()są same w sobie bezpieczne dla wątków, ale wartość size()jest oceniana oddzielnie od toArray(), a blokada listy nie jest utrzymywana między tymi dwoma wywołaniami.

Jeśli uruchomisz ten kod z innym wątkiem jednocześnie usuwającym elementy z listy, wcześniej czy później skończy się nowy String[]zwrócony, który jest większy niż wymagany do przechowywania wszystkich elementów na liście i ma wartości zerowe w ogonie. Łatwo jest myśleć, że ponieważ dwa wywołania metod do Listy występują w jednym wierszu kodu, jest to jakoś operacja atomowa, ale tak nie jest.


5
dobry przykład. Myślę, że nazwałbym to bardziej ogólnie „kompozycją operacji atomowych nie jest atomowa”. (Zobacz zmienną zmienną ++ dla innego prostego przykładu)
Alex Miller

29

Najczęstszym błędem, który widzimy w miejscu, w którym pracuję, jest to, że programiści wykonują długie operacje, takie jak wywołania serwera, na EDT, blokując GUI na kilka sekund i powodując brak reakcji aplikacji.


jedną z tych odpowiedzi, na którą chciałbym dać więcej niż jeden punkt
Epaga

2
EDT = Wątek
wysłania

28

Zapominanie o czekaniu () (lub Condition.await ()) w pętli, sprawdzanie, czy warunek oczekiwania jest rzeczywiście spełniony. Bez tego możesz napotkać błędy spowodowane fałszywymi pobudkami wait (). Zastosowanie kanoniczne powinno być:

 synchronized (obj) {
     while (<condition does not hold>) {
         obj.wait();
     }
     // do stuff based on condition being true
 }

26

Innym częstym błędem jest słaba obsługa wyjątków. Gdy wątek w tle zgłasza wyjątek, jeśli nie poradzisz sobie z nim właściwie, możesz w ogóle nie zobaczyć śladu stosu. A może zadanie w tle przestaje działać i nigdy się nie uruchamia ponownie, ponieważ nie udało się obsłużyć wyjątku.


Tak, i istnieją dobre narzędzia do obsługi tego teraz z programami obsługi.
Alex Miller

3
Czy możesz zamieścić linki do artykułów lub referencji, które wyjaśniają to bardziej szczegółowo?
Abhijeet Kashnia

22

Aż wziąłem klasę z Brian Goetz I nie zdawali sobie sprawy, że nie zsynchronizowane getterz prywatnym polu zmutowanego poprzez zsynchronizowane setterjest nie gwarantuje powrotu zaktualizowaną wartość. Tylko wtedy, gdy zmienna jest chroniona przez zsynchronizowany blok na obu odczytach ORAZ i zapisach , otrzymasz gwarancję najnowszej 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;  
    }
}

5
W późniejszych JVM (myślę, że 1.5 i dalej), użycie volatile również to naprawi.
James Schek

2
Niekoniecznie. volatile daje najnowszą wartość, więc zapobiega zwróceniu 1 na zawsze, ale nie zapewnia blokowania. Jest blisko, ale niezupełnie tak samo.
John Russell,

1
@JohnRussell Myślałem, że niestabilność gwarantuje relację przed wydarzeniem. czy to nie „blokowanie”? „Zapis do zmiennej zmiennej (pkt 8.3.1.4) v synchronizuje - ze wszystkimi kolejnymi odczytami v przez dowolny wątek (gdzie kolejne są zdefiniowane zgodnie z kolejnością synchronizacji).”
Shawn

15

Myślisz, że piszesz jednowątkowy kod, ale używasz zmiennych statycznych (w tym singletonów). Oczywiście będą one dzielone między wątkami. Zdarza się to zaskakująco często.


3
W rzeczy samej! Zmienna statyka przerywa ograniczanie wątku. Zaskakujące, nigdy nie znalazłem nic na temat tej pułapki ani w JCiP, ani w CPJ.
Julien Chastang

Mam nadzieję, że jest to oczywiste dla ludzi zajmujących się programowaniem równoległym. Stan globalny powinien być pierwszym miejscem do sprawdzania bezpieczeństwa wątków.
gtrak

1
@Gary Thing jest to, że nie myślą, że prowadzą równoległe programowanie.
Tom Hawtin - tackline

15

Arbitralnych wywołań metod nie należy wykonywać zsynchronizowanych bloków.

Dave Ray poruszył tę kwestię w swojej pierwszej odpowiedzi i faktycznie spotkałem się również z impasem związanym z wywoływaniem metod w słuchaczach z poziomu metody zsynchronizowanej. Myślę, że bardziej ogólna lekcja jest taka, że ​​wywołania metod nie powinny być wykonywane „na dziko” z poziomu zsynchronizowanego bloku - nie masz pojęcia, czy wywołanie będzie długotrwałe, spowoduje zakleszczenie, czy cokolwiek innego.

W tym przypadku, i zazwyczaj ogólnie, rozwiązaniem było ograniczenie zakresu synchronizowanego bloku, aby chronić tylko krytyczną prywatną sekcję kodu.

Ponadto, ponieważ teraz uzyskiwaliśmy dostęp do kolekcji słuchaczy poza zsynchronizowanym blokiem, zmieniliśmy ją na kolekcję kopiowania przy zapisie. Albo moglibyśmy po prostu stworzyć obronną kopię Kolekcji. Chodzi o to, że zwykle istnieją alternatywne sposoby bezpiecznego dostępu do zbioru nieznanych obiektów.


13

Najnowszym błędem związanym z Współbieżnością był obiekt, który w swoim konstruktorze utworzył usługę ExecutorService, ale kiedy obiekt nie był już przywoływany, nigdy nie zamknął usługi ExecutorService. Tak więc w ciągu tygodni tysiące wycieków wyciekło, ostatecznie powodując awarię systemu. (Technicznie rzecz biorąc, nie zawiesił się, ale przestał działać poprawnie, ale nadal działa.)

Technicznie przypuszczam, że nie jest to problem z współbieżnością, ale jest to problem związany z korzystaniem z bibliotek java.util.concurrency.


11

Niezrównoważona synchronizacja, szczególnie w przypadku map, wydaje się dość częstym problemem. Wiele osób uważa, że ​​synchronizacja przy wstawianiu do mapy (nie ConcurrentMap, ale powiedzmy HashMap) i niezsynchronizowanie przy pobieraniu jest wystarczające. Może to jednak prowadzić do nieskończonej pętli podczas ponownego mieszania.

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


11

Wystąpił problem współbieżności z serwletami, gdy istnieją zmienne pola, które zostaną ustawione przy każdym żądaniu. Ale dla każdego żądania istnieje tylko jedna instancja serwletu, więc działało to doskonale w środowisku jednego użytkownika, ale gdy więcej niż jeden użytkownik zażądał serwletu, wystąpiły nieprzewidziane 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 ...
    }
}

10

Niezupełnie błąd, ale najgorszym grzechem jest udostępnienie biblioteki, z której zamierzają korzystać inni ludzie, ale nie stwierdzenie, które klasy / metody są bezpieczne dla wątków, a które należy wywołać tylko z jednego wątku itp.

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


9

Moim największym problemem zawsze były impasy, szczególnie spowodowane przez słuchaczy, którzy zostali zwolnieni z trzymanym zamkiem. W takich przypadkach bardzo łatwo jest uzyskać odwrócone blokowanie między dwoma wątkami. W moim przypadku między symulacją uruchomioną w jednym wątku a wizualizacją symulacji uruchomionej w wątku interfejsu użytkownika.

EDYCJA: Przeniesiono drugą część do oddzielnej odpowiedzi.


Czy potrafisz podzielić ostatnią na osobną odpowiedź? Zatrzymajmy 1 na post. To są dwa naprawdę dobre.
Alex Miller,

9

Rozpoczęcie wątku w konstruktorze klasy jest problematyczne. Jeśli klasa zostanie rozszerzona, wątek można uruchomić przed wykonaniem konstruktora podklasy .


8

Zmienne klasy we wspólnych 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");

Kiedy tak się dzieje, kod jest znacznie bardziej skomplikowany 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ć niektóre klasy jako niezmienne, a niektóre struktury danych jako przechowujące tylko niezmienne obiekty.


8

Synchronizacja z literałem ciągów lub stałą zdefiniowaną przez literał ciągów jest (potencjalnie) problemem, ponieważ literał ciągu jest internowany i będzie współdzielony przez każdą inną osobę w JVM używającą tego samego literału ciągu. Wiem, że ten problem pojawił się w serwerach aplikacji i innych scenariuszach „kontenerowych”.

Przykład:

private static final String SOMETHING = "foo";

synchronized(SOMETHING) {
   //
}

W takim przypadku każdy, kto używa ciągu „foo” do zablokowania, dzieli tę samą blokadę.


Potencjalnie jest zamknięty. Problem polega na tym, że semantyka na KIEDY ciągi są internowane jest niezdefiniowana (lub, IMNSHO, niedefiniowana). Wewnętrzna stała kompilatora „foo” jest internowana, „foo” przychodząca z interfejsu sieciowego jest internalizowana tylko wtedy, gdy to zrobisz.
Kirk Wylie

Właśnie dlatego właśnie użyłem literałowej stałej łańcuchowej, która ma gwarancję internowania.
Alex Miller

8

Wierzę, że w przyszłości głównym problemem związanym z Javą będą (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 i nastroju JavaVM. Dziś szanse na „a” bycie 1 są bardzo duże. Ale na przyszłych maszynach NUMA może być inaczej. Wiele osób nie zdaje sobie z tego sprawy i uważa, że ​​nie muszą przejmować się wielowątkowością podczas fazy inicjalizacji.


Uważam to za nieco zaskakujące, ale wiem, że jesteś mądrym facetem Timem, więc wezmę to bez odniesienia. :) Jeśli jednak byłby ostateczny, nie byłby to problem, prawda? Byłbyś wtedy związany ostateczną semantyką zamrażania podczas budowy?
Alex Miller

W JMM wciąż znajduję rzeczy, które mnie zaskakują, więc nie ufałbym mi, ale jestem tego pewien. Zobacz także cs.umd.edu/~pugh/java/memoryModel/… . Gdyby pole było ostateczne, nie byłoby problemu, to byłoby widoczne po fazie inicjalizacji.
Tim Jansen

2
Jest to tylko problem, jeśli odwołanie do świeżo utworzonej instancji jest używane już przed zwróceniem / zakończeniem konstruktora. Na przykład klasa rejestruje się podczas budowy w publicznej puli i inne wątki zaczynają do niej uzyskiwać dostęp.
ReneS

3
MyClass.a oznacza dostęp statyczny, a „a” nie jest statycznym członkiem MyClass. Poza tym jest to, jak mówi „ReneS”, jest to tylko problem, jeśli wycieknie odniesienie do niekompletnego obiektu, na przykład dodanie „this” do jakiejś zewnętrznej mapy w konstruktorze.
Markus Jevring

7

Najgłupszym błędem, który często popełniam, jest zapominanie o synchronizacji przed wywołaniem powiadomienia () lub oczekiwania () na obiekcie.


8
W przeciwieństwie do większości problemów związanych z współbieżnością, nie jest to łatwe do znalezienia? Przynajmniej dostajesz tutaj IllegalMonitorStateException ...
Outlaw Programmer

Na szczęście bardzo łatwo go znaleźć ... ale to wciąż głupi błąd, który marnuje mój czas bardziej niż powinien :)
Dave Ray

7

Używanie lokalnego „nowego obiektu ()” jako muteksu.

synchronized (new Object())
{
    System.out.println("sdfs");
}

To jest bezużyteczne.


2
Jest to prawdopodobnie bezużyteczne, ale synchronizacja w ogóle ma kilka interesujących rzeczy ... Z pewnością tworzenie nowego obiektu za każdym razem jest kompletnym marnotrawstwem.
DRZEWO

4
To nie jest bezużyteczne. To bariera pamięci bez zamka.
David Roussel

1
@David: jedyny problem - jvm może go zoptymalizować, usuwając w ogóle taką blokadę
yetanothercoder

@insighter Widzę, że twoja opinia jest udostępniona ibm.com/developerworks/java/library/j-jtp10185/index.html Zgadzam się, że to głupie, ponieważ nie wiesz, kiedy twoja bariera pamięci zostanie zsynchronizowana, ja właśnie wskazywałam, że robi coś więcej niż nic.
David Roussel,

7

Innym częstym 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 StringBufferlub nawet java.util.Vector(jako lokalnych zmiennych metody).


1
Nie stanowi to problemu, ale jest niepotrzebne, ponieważ informuje JVM o synchronizacji danych z pamięcią globalną i dlatego może działać źle na wielu procesorach, nawet jeśli nikt nie używa bloku synchronizacji w tym samym czasie.
ReneS,

6

Wiele obiektów, które są chronione zamkiem, ale są często dostępne kolejno. Natknęliśmy się na kilka przypadków, w których blokady są uzyskiwane przez inny kod w różnych zamówieniach, co powoduje impas.


5

Nie zdając sobie sprawy, że thisklasa wewnętrzna nie należy thisdo klasy zewnętrznej. Zazwyczaj w anonimowej klasie wewnętrznej, która implementujeRunnable . Główny problem polega na tym, że ponieważ synchronizacja jest częścią wszystkich Objects, w rzeczywistości nie ma sprawdzania typu statycznego. Widziałem to co najmniej dwa razy na usenecie, i pojawia się również w Brian Goetz'z Java Concurrency in Practice.

Zamknięcia BGGA nie cierpią z tego powodu, ponieważ nie ma thistakiego zamknięcia ( thisodnosi się do klasy zewnętrznej). Jeśli użyjesz nie- thisobiektów jako zamków, obejdzie ten problem i inne.


3

Zastosowanie obiektu globalnego, takiego jak zmienna statyczna do blokowania.

Prowadzi to do bardzo złej wydajności z powodu niezgody.


Czasami czasami nie. Gdyby to było takie proste ...
gimpf

Zakładając, że wątkowanie w ogóle pomaga zwiększyć wydajność dla danego problemu, zawsze zmniejsza wydajność, gdy tylko więcej niż jeden wątek uzyskuje dostęp do kodu chronionego przez blokadę.
kohlerm

3

Miód? Przed nadejściem java.util.concurrentnajczęstszym problemem, na który rutynowo natknąłem się, było to, co nazywam „przebijaniem wątków”: aplikacje, które używają wątków do współbieżności, ale spawnują ich zbyt wiele i kończą się przeładowaniem.


Czy sugerujesz, że napotkasz więcej problemów, gdy java.util.concurrent jest dostępny?
Andrzej Doyle
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.