2015-01-08 11 views
12

Nel mio programma ho due metodi:Serrature tra due metodi che muoiono di fame un metodo

public void methodA() { //gets called very often 
    //write something to file 
} 

public void methodB() { 
    //write something to file 
} 

methodA viene chiamato dal cliente molto spesso, mentre methodB viene chiamato solo di tanto in tanto. Tuttavia, devo assicurarmi che ogni volta che un cliente voglia chiamare il numero methodB, può farlo (dopo l'eventuale esecuzione corrente di methodA è terminata). Ho provato a introdurre un blocco sincronizzato con un oggetto di blocco all'interno di ogni metodo, tuttavia methodB sembra morire di fame, dal momento che methodA viene chiamato più spesso. Come posso risolvere questo problema?

+0

use executor framework –

+1

non è possibile utilizzare i semafori per proteggere le chiamate? – ha9u63ar

+0

che tipo di dipendenza tra due metodi? – SMA

risposta

10

Sembra che tu abbia bisogno di una fiera Lock. Per effettuare uno di questi è necessario passare true come parametro al costruttore.

Lock lock = new ReentrantLock(true); 

public void methodA() { 
    lock.lock(); 
    try { 
     // Write something to a file. 
    } finally { 
     lock.unlock(); 
    } 
} 

public void methodB() { 
    lock.lock(); 
    try { 
     // Write something to a file. 
    } finally { 
     lock.unlock(); 
    } 
} 
+1

Grazie. In che modo la soluzione 1 garantisce che il metodo B non muoia di fame? Se utilizzo le serrature in base alla prima soluzione, il comportamento è lo stesso. Poiché il metodoA viene chiamato molto spesso, non tutte le chiamate di metodoB hanno successo. – Moonlit

+2

@ user1291235 - L'uso dell'opzione fair lock (passando 'true' al costruttore) dà la priorità al thread di attesa più lungo e dovrebbe evitare l'inedia. – OldCurmudgeon

+0

Oh capisco. Ora sembra funzionare. Grazie mille! – Moonlit

2

ReentrantLock ha un constructor con un parametro di equità che dovrebbe evitare la fame nel tuo caso.

1

Suggerisco di coinvolgere una coda con priorità. Semplicemente, due code, una per methodA e un'altra per methodB. Un altro thread che lavora sulla coda nella logica seguente: quando la coda per B non è vuota, gestirla, altrimenti, fare la coda per A.

+0

Nessuna necessità di due code. Uno solo per entrambi i metodi è sufficiente in quanto fornisce la stessa garanzia di progresso del blocco "giusto" menzionato in un'altra risposta. – JimmyB

1

Per questo è possibile utilizzare semaphores. Fondamentalmente funziona sull'idea di impostare un campo su un valore .. diciamo bloccato. e sull'altro metodo fai un po 'di tempo ... che si ripete infinito fino a quando l'altro processo è finito. È possibile utilizzare i semafori chiamando il metodo in un thread diffirent .. e utilizzare il vuoto sincronizzato parola chiave ..

I semafori sono in parte soluzioni hardware parzialmente software. Esistono anche soluzioni software pure. per esempio Peterson's algorithm

2

Se si desidera dare la priorità MethodB sopra Methoda, questa è la cosa più semplice che potevo venire con:

private Object writeLock = new Object(); 
private Object highPriorityLock = new Object(); 
private int highPriorityLockReleaseCount = 0; 
private int highPriorityLockLockCount = 0; 

public void methodA() { 
    synchronized (writeLock) { 
     synchronized (highPriorityLock) { 
      // Wait until there are no more highPriorityLocks 
      while (highPriorityLockLockCount != highPriorityLockReleaseCount) { 
       highPriorityLock.wait(); 
      } 
     } 
     // Do write (thread holds write lock) 
    } 
} 

public void methodB() { 
    synchronized (highPriorityLock) { 
     // Get current lock count 
     int lockCount = highPriorityLockLockCount; 
     // Increment lock count by one 
     highPriorityLockLockCount++; 
     // Wait until lock is acquired (current release count reaches saved lock count) 
     while (lockCount != highPriorityLockReleaseCount) { 
      highPriorityLock.wait(); 
     } 
    } 
    synchronized (writeLock) { 
     // Do write (thread holds write lock) 
    } 
    synchronized (highPriorityLock) { 
     // Relase high priority lock by incrementing current counter 
     highPriorityLockReleaseCount++; 
     highPriorityLock.notifyAll(); 
    } 
} 

Assicurati di gestire le eccezioni e per assicurarsi, che il blocco di alta priorità è sempre rilasciato correttamente

+0

Qual è lo stato iniziale delle variabili di istanza? Sono io o mi sembra eccessivo usare ** due ** blocchi del monitor, ** nidificando ** e ** due ** contatori di accesso per affrontare questo problema ?! –

+0

Penso che i valori iniziali dovrebbero essere ovvi, se comprendi l'algoritmo. Perché è eccessivo, dipende da quali sono le tue esigenze. Potrebbe essere che il semplice ReentrantLock sia sufficiente, ma non potrei determinarlo da determinati requisiti. –

+0

Annidare due serrature fa davvero suonare quella campana deadlock per me. Attenzione. – JimmyB

1

Se la vostra preoccupazione principale è con methodB invito a non essere bloccato o fame si poteva lasciare che i problemi concomitanti essere risolti non-blocking operazioni di i/o.

Java NIO.2 java.nio.channels.AsynchronousFileChannel in grado di fornire tali esigenze. Puoi trovare una buona spiegazione sull'uso e l'esempio here.

0

È all'incirca lo stesso di this question. La risposta più votata per quella domanda offre tre opzioni. Le opzioni 2 e 3 presuppongono entrambe che methodB sarà sempre chiamato dallo stesso thread, che non è stato detto, ma l'opzione 1 dovrebbe funzionare. Opzione 1 porting per Java è:

private final Lock m = new ReentrantLock(); 
private final Lock n = new ReentrantLock(); 
private final Lock l = new ReentrantLock(); 

public void methodA() { 
    l.lock(); 
    n.lock(); 
    m.lock(); 
    n.unlock(); 
    try { 
     doA(); 
    } finally { 
     m.unlock(); 
     l.unlock(); 
    } 
} 

public void methodB() { 
    n.lock(); 
    m.lock(); 
    n.unlock(); 
    try { 
     doB(); 
    } finally { 
     m.unlock(); 
    } 
} 

Questo dà MethodB priorità assoluta rispetto Methoda, a differenza di risposta di OldCurmudgeon, che dà sia la stessa priorità. Se si desidera anche che l'algoritmo sia equo (a parte la priorità di methodB su methodA), è necessario effettuare blocchi n e l.L'equità di m non è importante.