Многопоточность Java: создайте поток для запуска после проверки возвращаемого значения из первого потока

#java #multithreading

#java #многопоточность

Вопрос:

У меня есть поток, который в основном устанавливает соединение с сервером, и если соединение будет успешным, он вернет положительный идентификационный номер. Я хотел бы создать другой поток, который проверит, является ли текущий идентификационный номер положительным, и запустится, как только обнаружит, что идентификатор положительный.

 // My first thread that establishes connection 

new Thread() {
    public void run(){
          makeConnection();
          // this makeConnection method will make the ID become a positive number if the connection is fully established.
    }
}.start();
  

Обратите внимание, что obj.getCurrentId() возвращает текущий идентификационный номер. Но я изо всех сил пытаюсь написать второй поток и то, как он взаимодействует с первым потоком. Может ли кто-нибудь любезно помочь мне, пожалуйста? Спасибо.

Комментарии:

1. Итак, вы хотите использовать соединение только после успешного создания соединения. Почему бы не использовать один поток для выполнения обоих? Потоки предназначены для параллельных задач.

2. Потому что требуется время для фактического подключения к серверу и для того, чтобы идентификатор стал положительным. Если я помещу обе задачи в один поток, я боюсь, что код «проверить, является ли идентификатор положительным», может быть выполнен, когда полное соединение еще не полностью установлено.

3. Итак, почему наличие двух потоков решило бы эту проблему. Если вы используете один поток, вы, по крайней мере, знаете, что не можете использовать соединение, пока makeConnection() не вернется.

4. Вы описываете последовательное выполнение. Для этого нет причин использовать два отдельных потока. «Я боюсь, что код «проверить, является ли идентификатор положительным», может быть выполнен, когда полное соединение еще не полностью установлено» не имеет смысла.

Ответ №1:

Предполагая, что вы используете Java 8 хороший способ реализовать это с CompletableFuture , поскольку это позволит вам определить поток асинхронных задач для выполнения.

Так, например, здесь основной код может быть:

 // Call connect asynchronously using the common pool from a given thread
// then execute someMethod using another thread
CompletableFuture.supplyAsync(MyClass::connect)
    .thenCompose(MyClass::someMethodAsync);
  

Метод connect класса MyClass может быть:

 public static int connect() {
    try {
        SomeClass obj = makeConnection();
        // ok so we return a positive value
        return obj.getCurrentId();
    } catch (Exception e) {
        // Do something here
    }
    // ko so we return a negative value
    return -1;
}
  

Метод someMethodAsync класса MyClass может быть:

 public static CompletionStage<Void> someMethodAsync(int id) {
    return CompletableFuture.supplyAsync(() -> MyClass.someMethod(id));
}
  

Метод someMethod класса MyClass может быть:

 public static Void someMethod(int id) {
    if (id > 0) {
        // do something
    }
    return null;
}
  

Другим подходом может быть использование wait / notify / notifyAll или await / signal / signalAll для уведомления другого потока о том, что id изменилось.

Таким образом, ваш код может быть чем-то вроде этого:

 public class SomeClass {
    /**
     * The current id
     */
    private int currentId;
    /**
     * The object's monitor
     */
    private final Object monitor = new Object();

    /**
     * @return the current id
     */
    public int getCurrentId() {
        synchronized (monitor) {
            return this.currentId;
        }
    }

    /**
     * Sets the current id and notifies waiting threads
     */
    public void setCurrentId(final int currentId) {
        synchronized (monitor) {
            this.currentId = currentId;
            monitor.notifyAll();
        }
    }

    /**
     * Makes the calling thread wait until the id is positive
     * @throws InterruptedException if current thread is interrupted while waiting
     */
    public void waitForPositiveId() throws InterruptedException {
        synchronized (monitor) {
            while (currentId <= 0) {
                monitor.wait();
            }
        }
    }
}
  

Таким образом, ваш первый поток будет просто вызывать, makeConnection() предполагая, что внутренне он вызывает установщик setCurrentId из SomeClass , а второй поток начнется с вызова waitForPositiveId() , чтобы заставить его ждать, пока идентификатор не станет положительным.

ПРИМЕЧАНИЕ: Этот подход заставит второй поток ждать вечно, если makeConnection() произойдет сбой.

Ответ №2:

Несколько предложений:

  1. Создайте ExecutorService
  2. Отправьте первую задачу: ConnectionTask и получите результат
  3. Отправьте вторую задачу: ValidationTask и получите результат
  4. В зависимости от результата вы можете предпринять следующий набор действий.

Пример кода:

 import java.util.concurrent.*;
import java.util.*;

public class CallablePollingDemo{
    public CallablePollingDemo(){
        System.out.println("creating service");
        ExecutorService service = Executors.newFixedThreadPool(2);      
        try{
            Future future1 = service.submit(new ConnectionTask());  
            int result1 = ((Integer)future1.get()).intValue();
            System.out.println("Result from ConnectionTask task:" result1);
            if ( result1 > 0){ // change this condition to suit your requirement
                Future future2 = service.submit(new ValidationTask(result1));  
                int result2 = ((Integer)future2.get()).intValue();
                System.out.println("Result from ValidationTask task:" result2);
            }

        }catch(Exception err){
            err.printStackTrace();
        }
        service.shutdown();
    }
    public static void main(String args[]){
        CallablePollingDemo demo = new CallablePollingDemo();
    }
    class ConnectionTask implements Callable<Integer>{

        public ConnectionTask(){

        }
        public Integer call(){
            int id = 1;
            // Add your business logic here , make connection, get the result
            return id;
        }
    }
    class ValidationTask implements Callable<Integer>{
        Integer id = 0;
        public ValidationTask(Integer val){
            this.id = val;
        }
        public Integer call(){
            // Add your verification result ehre
            if ( id > 0 ) {
                return id;
            }else{
                return -1;
            }
        }
    }
}
  

Ответ №3:

Я рекомендую использовать интерфейс ExecutorService with Callable — просто верните свой идентификационный номер в Future результате.

Взгляните на ExecutorService.html#submit