2012-11-15 9 views
9

Więc mam kodu podobnego do tegoJak zdobyć wiele zamków bez ograniczania zamówień w Javie?

synchronized(objectOne){ do stuff } 
synchronized(objectTwo){ do stuff } 

Problem polega na tym, program będzie czekać na zamek na , nawet jeśli blokada objectTwo jest dostępna. Próbuję to powiedzieć: spróbuj zablokować zarówno , jak i objectTwo i niezależnie od tego, którą blokadę otrzymasz, najpierw wykonaj te czynności dla tej blokady. Wymyśliłem rozwiązanie, ale myślę, że jest raczej hacky i zastanawiam się, czy ktoś ma lepsze pomysły.

Oto mój pomysł: Rozpocznij 2 wątki, każdy czeka na blokadę, a główny wątek będzie czekał na CountDownLatch. Więc skończyć z czymś takim:

CountDownLatch latch = new CountDownLatch(2); 

new Thread(new Runnable(){ 
public void run(){ 
    synchronized(objectOne) { do stuff } 
    latch.countDown(); 
}).start(); 

new Thread(new Runnable(){ 
public void run(){ 
    synchronized(objectTwo) { do stuff } 
    latch.countDown(); 
}).start(); 

latch.await(); 
+1

To brzmi jak rozsądne podejście do mnie. – ArtB

+0

Nie mogę powiedzieć o opublikowanym kodzie, ale z mojego doświadczenia wynika, że ​​klasa, która blokuje dwa różne obiekty powinna prawie zawsze być podzielona na dwie klasy, z blokadą zamkniętą ** wewnątrz ** dwie klasy, a nie klasą, która agreguje one – Nate

+0

używasz dwóch różnych blokad, więc wątek B może nie widzieć zmiany pamięci przez wątek A, podejrzewam, że nie będzie żadnego zdarzenia - przedtem tutaj. –

Odpowiedz

5

myślę, że należy użyć Lock które da Ci metody boolean tryLock().

Powroty: true jeśli blokada została przejęta i fałsz w przeciwnym razie

przystąpić robić rzeczy, gdy masz co najmniej jeden z zamków.

+0

A więc sugerujesz coś takiego, jak wielokrotne próbowanie obu blokad, dopóki nie otrzymasz jednego z nich, zamkniętego przez jakąś pętlę oczekiwania? – user1825426

2

Użytkownik może chcieć mieć 2 kolejki zadań, 2 wątki, każdy odpytuje kolejkę i wykonuje zadania.

Dla zadań związanych z objectOne, umieszczasz go w kolejce # 1; zadania związane z objectTwo w kolejce # 2.

worker1.queue.put(new Runnable(){ public void run() { do stuff } }); 
worker2.queue.put(new Runnable(){ public void run() { do stuff } }); 

---- 

class Worker extends Thread 

    BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(); 

    public void run() 
     while(true) 
      queue.take().run(); 
0

W zależności od liczby stuff może być więcej narzut, aby wydzielić wiele wątków, aby zrobić rzeczy. Najlepiej jest robić rzeczy w jednym wątku, jeśli stuff jest wystarczająco szybką operacją. Będziesz musiał to zrobić, aby wiedzieć.

0

Podobają mi się twoje hacki, przynajmniej jeśli jest to jednorazowa sytuacja. To powiedziawszy ...

Jeśli robisz tego rodzaju rzeczy bardzo często i chcesz czegoś "mniej hacky", proponuję ExecutorService#invokeAll(). Spowoduje to pobranie listy Kaloszów, wykonanie ich w puli wątków i bloków, dopóki wszystkie nie zostaną ukończone.

Szkic:

ExecutorService es = Executors.newCachedThreadPool(); // for example... 
List<Future<Void>> results = es.invokeAll(new ArrayList {{ 
     add(new Callable<Void> { 
      public Void call() { synchronized(objectOne) { do stuff } } 
     }); 
     add(new Callable<Void> { 
      public Void call() { synchronized(objectTwo) { do stuff } } 
     }); 
    }}); 
// both Callables are done when you get here 

To oczywiście zakłada, że ​​to OK, aby wywołać te metody z różnych wątków w tym momencie w swojej aplikacji. Jeśli z jakiegoś powodu musisz zadzwonić do obu z tego samego wątku, myślę, że jesteś skazany na użycie tryLock i zajęty-czekanie, jak to omówiono w odpowiedzi Bhesha Gurunga.

Powiązane problemy