#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:
Несколько предложений:
- Создайте ExecutorService
- Отправьте первую задачу:
ConnectionTask
и получите результат - Отправьте вторую задачу:
ValidationTask
и получите результат - В зависимости от результата вы можете предпринять следующий набор действий.
Пример кода:
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