2012-06-25 13 views
5

Jestem zakłopotany synchronizacją metody instancji i metody statycznej. Chcę napisać bezpieczną klasę gwintu następujące:Synchronizacja w metodzie statycznej i instancji

public class safe { 

    private final static ConcurrentLinkedQueue<Object> objectList= 
     new ConcurrentLinkedQueue<Object>(); 

    /** 
    * retrieves the head of the object and prints it 
    */ 
    public synchronized static void getHeadObject() { 
     System.out.println(objectList.peek().toString()); 

    } 

    /** 
    * creates a new object and stores in the list. 
    */ 
    public synchronized void addObject() { 
     Object obj=new Object(); 
     objectList.add(obj); 

    } 
} 

Synchronizacja na metodzie statycznej zablokuje na safe.class blokady i synchronizacji na metodzie instancji zostanie zablokowana na tym .i stąd niespójnym stanie zostanie osiągnięty .

Czy mogę osiągnąć spójny stan poniższego fragmentu kodu, jak to osiągnąć?

+1

Dlaczego 'addObject' metodę instancji? Dlaczego nie statyczne? Dlaczego mimo to synchronizujesz wokół obiektu współbieżnego? Są już bezpieczne dla wątków. – Wug

+0

Co masz na myśli przez Niekonsekwentny stan? –

+0

Zakładam, że miał na myśli 'Kolejka objectList = new List ' zamiast 'ConcurrentLinkedQueue objectList = new ConcurrentLinkedQueue ', więc otrzymujemy odpowiedź na pytanie, które on sam zadał. –

Odpowiedz

1

EDYCJA: Zakładam, że oznaczało to Queue<Object> objectList zamiast ConcurrentLinkedQueue<Object> objectList. ConcurrentLinkedQueue<Object> robi już dla Ciebie całą gwarancję dotyczącą nici, co oznacza, że ​​możesz dzwonić pod numer objectList.peek(), nie tracąc z oczu warunków wyścigu. Jest to świetne, jeśli tworzysz programy wielowątkowe, ale nie jest to zbyt dobre dla nauki o bezpieczeństwie wątków.

Twoje metody nie muszą być synchronized, zakładając, że masz jeden wątek działający na jednej instancji obiektu naraz, ale jednak jeśli potrzebujesz wielu instancji klasy, które wszystkie odnoszą się do tej samej zmiennej klasy statycznej, możesz trzeba synchronized nad zmiennej klasy tak:

public static void getHeadObject() { 
    synchronized(safe.objectList) { 
     System.out.println(objectList.peek().toString()); 
    } 
} 

to blokuje objectList i nie pozwala na to, aby być odczytywane lub zapisywane w żadnym innym wątku, jak tylko program jest wewnątrz bloku synchronizacji. Zrób to samo dla wszystkich innych metod, aby uzyskać synchronized.

UWAGA:

Jednakże, ponieważ robisz tylko jedną prostą operację get List.peek(), naprawdę nie trzeba synchronizować nad objectList ponieważ w wyścigu, będzie to uzyskać albo jedną wartość List lub innym. Problem z warunkami wyścigu ma miejsce, gdy wykonywanych jest wiele złożonych operacji odczytu/zapisu, przy czym zmienia się między nimi wartość.

Na przykład, jeśli miał klasę PairInt z PairInt.x i PairInt.y pól, z ograniczeniem, że x = 2y i chciał zrobić

System.out.println(myIntPair.x.toString() + ", " + myIntPair.y.toString()); 

i inny wątek został aktualizujący wartość x i y w w tym samym czasie,

myIntPair.y = y + 3; 
myIntPair.x = 2 * y; 

I write wątek zmodyfikowany myIntPair w przerwie między gwint odczytu na myIntPair.x.toString() i myIntPair.y.toString() możesz otrzymać dane wyjściowe, które wyglądają jak (10, 8), co oznacza, że ​​działasz przy założeniu, że x == 2 * y może spowodować awarię twojego programu.

W takim przypadku Twój odczytu musi użyć synchronized, ale dla bardziej prostszych rzeczy jak peek() na prostym object które jest dodawane lub usuwane, niemodyfikowane podczas gdy w kolejce The synchronized może, w większości przypadków zostać upuszczonym. W rzeczywistości, dla stanu string, int, bool i tym podobnych, warunek synchronized dla prostego odczytu powinien zostać usunięty.

Jednak zapisy powinny zawsze być synchronized na operacjach, które nie są jawnie bezpieczne dla wątków, tj. Już obsługiwane przez java. I jak najszybciej zdobyć więcej niż jednego zasobu, lub wymagać, aby Twój zasób pozostają takie same w całej operacji, jak to zrobić wiele wierszy logiki do niego, wtedy musi używaćsynchronized

+0

On już używa zsynchronizowanego słowa kluczowego w deklaracji 'getHeadObject', która po prostu synchronizuje na safe.class. – Wug

+0

Myślę, że Hans jest poprawny, ponieważ musimy być bardzo ostrożni w takich sytuacjach, każda metoda synchronicznej instancji powinna mieć jawnie zsynchronizowany blok dostępu do pól statycznych, jeśli statyczne pola wymagają wyłącznego dostępu. –

+0

To wcale nie pomaga ... dwa bezpieczne obiekty będą miały dostęp do tej samej listy obiektów, a jeśli synchronizujesz oba obiekty, 'zsynchronizowany' pozwoli ci pisać równocześnie z obiema, ponieważ nie jesteś jawnie za pomocą (o ile vm może powiedzieć), ten sam obiekt. –

2

Najpierw ConcurrentLinkedQueue nie wymaga wyraźnej synchronizacja. Zobacz this answer.

Po drugie, zawsze można synchronizować przedmiotu uzyskiwania dostępu:

public class safe { 

     private final static ConcurrentLinkedQueue<Object> objectList= 
      new ConcurrentLinkedQueue<Object>(); 

     /** 
     * retrieves the head of the object and prints it 
     */ 
    public static void getHeadObject() { 
     synchronized(objectList){ 
      System.out.println(objectList.peek().toString()); 
     } 

    } 

     /** 
     * creates a new object and stores in the list. 
     */ 
    public void addObject() { 
      Object obj=new Object(); 
     synchronized(objectList){ 
      objectList.add(obj); 
     } 

    } 
} 
+0

@assylias: "kopiuj-wklej", przepraszam =) –

0

kilka uwag:

  • Java konwencje:
    • nazw klas powinny być w CamelCase (tj nazywają swoją klasę Safe, a nie safe)
    • pochodzi sprzed synchronized w deklaracji metody
    • static przychodzi przed final w deklaracji pola
  • jak inni już powiedział ConcurrentLinkedQueue jest już wątku bezpieczne, więc nie ma potrzeby synchronizacji w przykładzie dajesz.
  • Mieszanie metod statycznych i niestatycznych w taki sposób, w jaki wyglądasz dziwnie.
  • zakładając, że twoje rzeczywiste użycie jest bardziej skomplikowane i potrzebujesz metody do przeprowadzania operacji atomowych, Twój kod nie działa, jak zauważyłeś, ponieważ dwie zsynchronizowane metody nie synchronizują się na tym samym monitorze:
public static synchronized getHeadObject(){} //monitor = Safe.class 
public static synchronized addObject(){} //monitor = this 

tak aby odpowiedzieć na konkretne pytanie, można użyć oddzielny obiekt statyczny jako lock:

public class Safe { 

    private static final ConcurrentLinkedQueue<Object> objectList = 
      new ConcurrentLinkedQueue<Object>(); 
    // lock must be used to synchronize all the operations on objectList 
    private static final Object lock = new Object(); 

    /** 
    * retrieves the head of the object and prints it 
    */ 
    public static void getHeadObject() { 
     synchronized (lock) { 
      System.out.println(objectList.peek().toString()); 
     } 
    } 

    /** 
    * creates a new object and stores in the list. 
    */ 
    public void addObject() { 
     synchronized (lock) { 
      Object obj = new Object(); 
      objectList.add(obj); 
     } 
    } 
} 
Powiązane problemy