web-dev-qa-db-fra.com

Exécuter du code sur le thread principal à partir d'un thread secondaire?

Ceci est une question Java générale et non une première sur Android!

J'aimerais savoir comment exécuter du code sur le thread principal, à partir du contexte d'un thread secondaire. Par exemple:

new Thread(new Runnable() {
        public void run() {
            //work out pi to 1,000 DP (takes a while!)

            //print the result on the main thread
        }
    }).start();

Ce genre de chose - je me rends compte que mon exemple est un peu médiocre, car en Java, il n'est pas nécessaire d'être dans le fil principal pour imprimer quelque chose, et que Swing a également une file d'attente d'événements run say a Runnable sur le thread principal dans le contexte d'un thread d'arrière-plan.

EDIT: À titre de comparaison - voici comment je procéderais en Objective-C:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0UL), ^{
    //do background thread stuff

    dispatch_async(dispatch_get_main_queue(), ^{
        //update UI
    });
});

Merci d'avance!

26
Javawag

Il n'y a pas de moyen universel d'envoyer simplement du code à un autre thread en cours d'exécution et de dire "Hé, toi, fais ça." Vous auriez besoin de mettre le fil principal dans un état où il dispose d'un mécanisme pour recevoir du travail et attend que le travail soit terminé.

Voici un exemple simple d'installation du thread principal pour attendre de recevoir le travail d'autres threads et de l'exécuter au fur et à mesure de son arrivée. Évidemment, vous voudriez ajouter un moyen de mettre fin au programme et ainsi de suite ...!

public static final BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();

public static void main(String[] args) throws Exception {
    new Thread(new Runnable(){
        @Override
        public void run() {
            final int result;
            result = 2+3;
            queue.add(new Runnable(){
                @Override
                public void run() {
                    System.out.println(result);
                }
            });
        }
    }).start();

    while(true) {
        queue.take().run();
    }
}
26
Affe

Dans le cas où vous êtes sur Android, utiliser un gestionnaire devrait faire le travail?

new Handler(Looper.getMainLooper()).post(new Runnable () {
    @Override
    public void run () {
        ...
    }
});
19
HarryS

Une vieille discussion, mais s’il s’agit d’envoyer une demande au fil principal (une direction différente), vous pouvez également le faire avec des futures. L'objectif de base est d'exécuter quelque chose en arrière-plan et, une fois terminé, d'obtenir le résultat:

public static void main(String[] args) throws InterruptedException, ExecutionException {
    // create the task to execute
    System.out.println("Main: Run thread");
    FutureTask<Integer> task = new FutureTask<Integer>(
            new Callable<Integer>() {

                @Override
                public Integer call() throws Exception {
                    // indicate the beginning of the thread
                    System.out.println("Thread: Start");

                    // decide a timeout between 1 and 5s
                    int timeout = 1000 + new Random().nextInt(4000);

                    // wait the timeout
                    Thread.sleep(timeout);

                    // indicate the end of the thread
                    System.out.println("Thread: Stop after " + timeout + "ms");

                    // return the result of the background execution
                    return timeout;
                }
            });
    new Thread(task).start();
    // here the thread is running in background

    // during this time we do something else
    System.out.println("Main: Start to work on other things...");
    Thread.sleep(2000);
    System.out.println("Main: I have done plenty of stuff, but now I need the result of my function!");

    // wait for the thread to finish if necessary and retrieve the result.
    Integer result = task.get();

    // now we can go ahead and use the result
    System.out.println("Main: Thread has returned " + result);
    // you can also check task.isDone() before to call task.get() to know
    // if it is finished and do somethings else if it is not the case.
}

Si votre intention est d'exécuter plusieurs tâches en arrière-plan et d'extraire les résultats, vous pouvez définir des files d'attente comme indiqué ci-dessus ou vous pouvez diviser le processus en plusieurs futurs (en commençant tout d'un coup ou en commençant un nouveau si nécessaire, même à partir d'un autre futur) . Si vous stockez chaque tâche dans une carte ou une liste, initialisée dans le fil principal, vous pouvez vérifier les futures que vous voulez à tout moment et obtenir leurs résultats lorsqu'ils sont terminés.

7
Matthieu

Vous voudrez peut-être utiliser le «fil de distribution uniforme» où la plupart des événements liés aux événements se produisent. Si vous utilisez le swing, alors:

    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            Your code here.
        }
    });

Ou créez une classe qui implémente Runnable et transmettez-la à invokeLater ().

3
Mycenae

Si vous utilisez JavaFX, ce que je recommande vivement, vous pouvez utiliser

    Platform.runLater(new Runnable() {
        @Override
        public void run() {
            alert(text);
        }
    });

à partir de votre thread non-UI, et le exécutable sera exécuté à partir du thread UI au retour de votre thread.

0
Antinous