2014-10-07 17 views
5
public class MyStack2 { 
    private int[] values = new int[10]; 
    private int index = 0; 

    public synchronized void push(int x) { 
     if (index <= 9) { 
      values[index] = x; 
      Thread.yield(); 
      index++; 
     } 
    } 

    public synchronized int pop() { 
     if (index > 0) { 
      index--; 
      return values[index]; 
     } else { 
      return -1; 
     } 
    } 

    public synchronized String toString() { 
     String reply = ""; 
     for (int i = 0; i < values.length; i++) { 
      reply += values[i] + " "; 
     } 
     return reply; 
    } 
} 

public class Pusher extends Thread { 
    private MyStack2 stack; 

    public Pusher(MyStack2 stack) { 
     this.stack = stack; 
    } 

    public void run() { 
     for (int i = 1; i <= 5; i++) { 
      stack.push(i); 
     } 
    } 
} 

public class Test { 
    public static void main(String args[]) { 
     MyStack2 stack = new MyStack2(); 
     Pusher one = new Pusher(stack); 
     Pusher two = new Pusher(stack); 
     one.start(); 
     two.start(); 
     try { 
      one.join(); 
      two.join(); 
     } catch (InterruptedException e) { 
     } 
     System.out.println(stack.toString()); 
    } 
} 

Od metod MyStack2 klasy są zsynchronizowane, spodziewałem wyjście jako 1 2 3 4 5 1 2 3 4 5. Ale wyjście nieokreślony. Często daje: 1 1 2 2 3 3 4 4 5 5synchronization- Temat Kiedy nitki zwolnić blokadę na obiekcie

Zgodnie z moim rozumieniem, po uruchomieniu wątku jeden nabywa on blokadę metody push. Wewnątrz push() wątek jeden daje na jakiś czas. Ale czy zwalnia blokadę po wywołaniu yield()? Teraz, gdy wątek drugi zostanie uruchomiony, czy wątek dwóch uzyska blokadę, zanim wątek jeden zakończy wykonywanie? Czy ktoś może wyjaśnić, kiedy wątek jeden zwalnia blokada obiektu na stosie?

+0

Spójrz na ten http://stackoverflow.com/questions/18635616/yield-inside-synchronized-block-lock-release-after-calling-yield – SimY4

Odpowiedz

5

Metoda synchronized zatrzyma tylko inne wątki podczas jej wykonywania. Jak tylko zwróci, inne wątki mogą (i często będą od razu) uzyskiwać dostęp.

Scenariusz aby uzyskać 1 1 2 2 ... mogą być:

  1. gwintu 1 wywołań push(1) i pozostawia w
  2. Temat rozmowy push(1) i 2 jest zablokowana podczas gwintu 1 używa go..
  3. Wątek 1 kończy się push(1).
  4. Temat 2 uzyska dostęp do push i popycha 1 ale jednocześnie gwintu 1 Połączenia push(2).

Wynik 1 1 2 - możesz wyraźnie zobaczyć, jak to trwa.

+0

Tak, ale jeśli tak jest, wyjście programu nie powinno być jak: 1 1 2 2 3 3 4 4 5 5. Ale często jest to wyjście. – sam

+7

@javaTech - nie ma umowy, która mówi, że wątki Java powinny być uczciwe. Prawie każde zamówienie może być wynikiem twojego kodu. 1 1 2 2 ... jest całkiem poprawne. – OldCurmudgeon

1

Wygląda na to, że możesz mieć wątpliwości co do znaczenia słów kluczowych zsynchronizowanych i zwrotnych.

Synchronizacja oznacza, że ​​tylko jeden wątek może wejść do tego bloku kodu naraz. Wyobraź sobie, że jest to brama i potrzebujesz klucza, aby się przedostać. Każdy wątek w momencie wejścia przyjmuje jedyny klucz i zwraca go po zakończeniu. To pozwala następnemu wątkowi uzyskać klucz i wykonać kod wewnątrz. Nie ma znaczenia, jak długo są w metodzie zsynchronizowanej, tylko jeden wątek może wejść na raz.

Wydajność (I tak proponuje się to tylko sugestia) do kompilatora, że ​​obecny wątek można zrezygnować z przydzielonego czasu a druga nić może się rozpocząć wykonywanie. Jednak nie zawsze tak się dzieje.

W twoim kodzie, nawet jeśli bieżący wątek sugeruje kompilatorowi, że może on zrezygnować z czasu wykonania, nadal posiada klucz do zsynchronizowanych metod, a zatem nowy wątek nie może wejść.

Nieprzewidywalne zachowanie wynika z wydajności, nie rezygnując z czasu wykonania zgodnie z przewidywaniami.

Mam nadzieję, że pomogło!

+0

"Synchronizacja oznacza, że ​​tylko jeden wątek może wchodzić do tego bloku kodu naraz." Jest to prawdą tylko wtedy, gdy kod zawsze próbuje zablokować _same object_ przy każdym wejściu do bloku. Więcej niż jeden wątek może wejść do tego samego bloku 'synchronized (foo) {...}', jeśli 'foo' nie jest stałą. –

+0

"Plon sugeruje ... kompilatorowi, że ..." Nie kompilatorowi. Kompilator nie zna różnicy między Thread.yield() a innym wywołaniem metody. Magia (jeśli występuje) dzieje się w czasie wykonywania, najprawdopodobniej wtedy, gdy wątek natywny w implementacji JVM dokonuje pewnego rodzaju wywołania systemowego "wydajność". –

+2

"Nieprzewidywalne zachowanie wynika z wydajności, która się nie poddaje ..." Nie sądzę, ponieważ, jak zauważyłeś, wywołanie yield() odbywa się wewnątrz zsynchronizowanego bloku. Bez względu na to, czy coś robi, czy nie, drugi wątek nie będzie działał przed zwróceniem yield(). Nieprzewidywalność wynika z faktu, że dwa wątki wielokrotnie walczą o tę samą blokadę. Załóżmy, że wątek A ma blokadę, a wątek B czeka na blokadę. Kiedy wątek A zwalnia blokadę, a następnie _immediately_ próbuje ją ponownie zablokować, który wątek dostaje blokadę? _ To jest to, czego nie można przewidzieć. –

2

Kiedy mówisz:

Według mojego rozeznania, gdy jeden wątek jest uruchamiany go nabywa blokadę metodą push.

To nie jest w porządku, ponieważ zamek nie jest tylko metodą push.Blokada, której używa metoda push, znajduje się na instancji MyStack2, do której wywoływane jest polecenie push. Metody pop i toString używają tego samego zamka co push. Kiedy wątek wywoła którąkolwiek z tych metod na obiekcie, musi poczekać, aż będzie mógł uzyskać blokadę. Wątek w trakcie wywoływania push zablokuje kolejny wątek wywołania popu. Wątki wywołują różne metody uzyskiwania dostępu do tej samej struktury danych, przy użyciu tej samej blokady dla wszystkich metod dostępu do struktury uniemożliwiają wątkom równoczesny dostęp do struktury danych.

Gdy wątek oddaje blokadę przy wychodzeniu z synchronizowanej metody, planista decyduje, który wątek otrzyma blokadę następną. Twoje wątki przejmują blokady i pozwalają im jechać wiele razy, za każdym razem, gdy zostanie zwolniony blokada, decyzja do zrobienia jest zaplanowana. Nie można przyjąć żadnych założeń, które zostaną wybrane, może to być dowolna z nich. Dane wyjściowe z wielu wątków są zwykle pomieszane.

+1

"blokowanie zapobiega równoczesnemu dostępowi do struktury danych." Ostrożny! Nowicjusze mogą nie rozumieć, że to nie blokowanie _per se_ chroni dane. Muszą być informowani (czasami więcej niż raz), że to, co chroni dane, to fakt, że każdy blok kodu, który aktualizuje lub używa danych, blokuje tę samą blokadę. –

+0

@james: uzgodniony, przeredagowany. –