11
votes

Les appelants bloquent jusqu'à GETFOO () a une valeur prête?

J'ai un thread code> code> qui expose une propriété que d'autres threads veulent accéder:

class MyThread extends Thread {
   private Foo foo;
   ...
   Foo getFoo() {
     return foo;
   }
   ...
   public void run() { 
     ...
     foo = makeTheFoo();
     ...
   }
}


0 commentaires

9 Réponses :


0
votes

Vous pouvez utiliser les méthodes d'attente () et de notification ():

Exemple simple ici:

http://www.java-samples.com/showtutorial.php ? TutorialID = 306


2 commentaires

Certainement, je peux rouler une solution à la main. Je pense que c'est un peu plus complexe que celui-ci - le champ doit être volatile et nous avons besoin notifier () pour commencer - alors j'espérais des commentaires sur quoi d'autre besoin ou une solution pré-emballée qui a définitivement raison.


@San dans ce cas, j'irais avec la suggestion de Djclayworth



0
votes

Si je comprends bien, des choses concurrentes sont créées explicitement de ne pas attendre et faire ce que vous voulez tout de suite - mais si cela est possible du tout. Dans votre cas, vous devez attendre que quelque chose soit disponible, votre seule option est que votre seule option est, euh, to attendre () . En bref, il semble que votre façon de la décrire est la seule façon correcte.


0 commentaires

3
votes

En général notify () et notifytall () sont les méthodes souhaitées. Notifier () est dangereux si un seul élément crée le FOO et de nombreux threads pourraient l'attendre. Mais je pense qu'il y a d'autres problèmes ici.

Je ne ferais pas le fil le lieu de stocker le foo. Cela signifie que vous devez garder un filetage après la création de FOO. Pourquoi ne pas faire un autre objet pour stocker le FOO et avoir le fil de création d'écrire?

Alors j'aurais getfoo () testez FOO et qu'attendez seulement si c'était non NULL (n'oubliez pas de le synchroniser avec lui-même et avec le Setter FOO).


1 commentaires

D'accord, je souhaite que ce n'était pas si. Dans ce cas, je dois vraiment faire l'objet dans la course (). C'est une application d'interface graphique avec des besoins étranges.



16
votes

Si foo code> est initialisé une seule fois, un CountownLatch code> est un excellent ajustement.

class MyThread extends Thread {

  private final CountDownLatch latch = new CountDownLatch(1);

  ...

  Foo getFoo() throws InterruptedException
  {
    latch.await(); /* Or use overload with timeout parameter. */
    return foo;
  }

  @Override
  public void run() {
    foo = makeTheFoo()
    latch.countDown();
  }

}


0 commentaires

0
votes

Si l'initialisation est une offre unique, essayez Java.Util.ConCurrent.CountTownlatch .


0 commentaires

0
votes

est une initialisation paresseuse une option? XXX


1 commentaires

Pas dans ce cas que la FOO doit être faite dans l'autre thread.



1
votes

Essayez le Nombre de comptownlatch Code>:

class MyThread extends Thread {
   private volatile CountDownLatch latch;
   private Foo foo;
   MyThread(){
      latch = new CountDownLatch(1);
   }
   ...
   Foo getFoo() {
     latch.await(); // waits until foo is ready
     return foo;
   }
   ...
   public void run() { 
     ...
     foo = makeTheFoo();
     latch.countDown();// signals that foo is ready
     ...
   }
}


1 commentaires

En fait, il y a un bogue subtil ici: étant donné que loquet n'est pas final ou volatile , il n'est pas garanti d'être "visible" à d'autres threads. Pourrait ne pas être un problème dans la pratique, mais la garantie ne coûte rien vraiment.



1
votes

J'utiliserais l'un des blockingQueue code> dans java.util.concurrent code> plus précisément, s'il y a un fil en attente de foo et l'un produisant, je l'utilise synchronousquee code> En cas de plus de producteurs et / ou plus de consommateurs, mon option par défaut est un LinkedblockingQueue code> , mais d'autres implémentations peuvent être mieux adaptées à votre application. Votre code devient alors:

class MyThread extends Thread {
   private SynchronousQueue<Foo> queue = new SynchronousQueue<Foo>();
   ...
   Foo getFoo() {
     Foo foo;
     try {
        foo = queue.take();
     }
     catch (InteruptedException ex) {
        ...stuff ...
     }
     return foo;
   }
   ...
   public void run() { 
     ...
     foo = makeTheFoo();
     try {
        queue.put(foo);
     }
     catch (InteruptedException ex) {
        ...stuff ...
     }
     ...
   }
}


2 commentaires

Synchronousqueue est une construction pratique dans de nombreux cas, mais elle ne convient pas à l'exigence ici très bien: foo est uniquement initialisé une fois et lisait éventuellement plusieurs fois. Cela n'autoriserait que foo à lire une fois. D'autres tentatives de lecture de la propriété bloqueraient pour toujours.


Oui, vous avez raison, j'avais manqué la partie "uniquement initialisée une fois". Je pensais que c'était plus une situation de producteur / consommateur.



0
votes

Peut-être essayez peut-être ma classe FutureValue ...

import java.util.concurrent.CountDownLatch;

public class FutureValue<T> {
private CountDownLatch latch = new CountDownLatch(1);
private T value;

public void set(T value) throws InterruptedException, IllegalStateException {
    if (latch.getCount() == 0) {
        throw new IllegalStateException("Value has been already set.");
    }
    latch.countDown();
    this.value = value;
}

/**
 * Returns the value stored in this container. Waits if value is not available.
 * 
 * @return
 * @throws InterruptedException
 */
public T get() throws InterruptedException {
    latch.await();
    return value;
}

}

// Usage example
class MyExampleClass {
@SuppressWarnings("unused")
private static void usageExample() throws InterruptedException {
    FutureValue<String> futureValue = new FutureValue<>();

    // the thread that will produce the value somewhere
    new Thread(new Runnable() {

        @Override
        public void run() {
            try {
                futureValue.set("this is future");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }).run();

    String valueProducedSomewhereElse = futureValue.get();
}
}


0 commentaires