3

J'ai une API comme celle-ci, où Baz est l'implémentation du worker. Ce Bar doit être thread-safe, et cela devient compliqué lors de l'interaction avec les rappels de Baz.Maintenir la sécurité des threads tout en empêchant les interblocages dus à un éventuel rappel synchrone

L'instance baz actuelle doit être référencée dans le rappel (qui peut être appelé sur un thread de travail ou de manière synchrone). Les commentaires devraient montrer le problème:

final class Bar { 
    final Lock lock = new ReentrantLock(); 
    Baz baz; // Guarded by lock. 

    void run() { // Called by any thread. 
    lock.lock(); 
    if (baz.isRunning()) { 
     lock.unlock(); 
     return; 
    } 
    baz = new Baz(); 
    // If it unlocks here, the next line may execute on the wrong Baz. 
    // If it doesn't unlock here, there will be a deadlock when done() is called synchronously. 
    // lock.unlock(); 
    baz.run(new Baz.Callback() { // May be called synchronously or by Baz worker thread. 
     @Override 
     public void done() { 
     lock.lock(); 
     baz = new Baz(); 
     lock.unlock(); 
     } 
    }); 
    } 
} 

est-il un bon moyen de faire correctement ce travail tout en ne provoquant pas une impasse?

Edit: plus succinctement:

final class Foo { 
    final Lock lock = new ReentrantLock(); 

    void run() { 
    lock.lock(); 
    worker.enqueue(new Callback() { 
     @Override void complete() { 
     lock.lock(); // Could cause deadlock. 
     } 
    }); 
    lock.unlock(); 
    } 
} 

Répondre

0

Je ne sais pas à completelly obtenir ce que vous essayez d'atteindre, mais peut-être c'est ce que vous cherchez?

final class Bar { 
    final Lock lock = new ReentrantLock(); 
    Baz baz = new Baz(); 

    void run() { 
     if (!lock.tryLock()) { 
      return; 
     } 
     try { 
      CountdownLatch callbackFlag = new CountdownLatch(1); 
      baz.run(new Baz.Callback() { 
       @Override 
       public void done() { 
        callbackFlag.countDown(); 
       } 
      }); 
      try { 
       callbackFlag.await(); // better use overloaded method with max timeout waiting. you don't probably want to wait forever 
       baz = new Baz(); // do you really want to reinit Baz on each execution? 
      } catch (InterruptedException e) { 
       // decide what you want to happen here 
      } 
     } finally { 
      lock.unlock(); 
     } 
    } 
} 
+0

L'affectation baz doit cependant être dans le rappel. –

+0

édité pour clarifier. –

+0

pourquoi devrait-il être référencé dans le rappel? le code que j'ai posté assure que baz n'est pas reinit jusqu'à ce que le callback soit terminé –