#java #multithreading #concurrency #executorservice
#java #многопоточность #параллелизм #executorservice
Вопрос:
Хорошо, итак, я пытался преобразовать блокирующий сетевой запрос в неблокирующий запрос. Библиотека, которую я использую для сетевого ввода-вывода, предоставляет функции для выполнения асинхронных HTTP-вызовов, но в любом случае, ради экспериментов я попытался сделать это таким образом:
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;
import com.mashape.unirest.http.exceptions.UnirestException;
import com.mashape.unirest.request.GetRequest;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class TestExecutorService {
private static final ExecutorService executor = Executors.newSingleThreadExecutor();
static volatile Thread innerThread;
public static void asyncGet (String url) {
executor.execute(new Runnable() {
@Override
public void run() {
innerThread = Thread.currentThread();
GetRequest request = Unirest.get(url);
try {
HttpResponse <String> response = request.asString();
System.out.println(response.getBody());
Unirest.shutdown();
} catch (UnirestException exc) {
exc.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
});
}
}
public class Main {
public static void main(String[] args) {
TestExecutorService.asyncGet("https://stackoverflow.com");
System.out.println("We're already here!");
try {
// Delay so that executor service's thread object could be
// ...assigned to static variable innerThread
Thread.sleep(100);
TestExecutorService.innerThread.join();
} catch (InterruptedException ie) {
ie.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
}
Я не профессиональный программист и абсолютный новичок, когда дело доходит до параллелизма, и даже я мог бы сказать, что этот фрагмент кода можно улучшить, по крайней мере немного (одно из тех чувств, которые испытываешь, будучи новичком, когда знаешь, что что-то не так, но не уверен, что именно). В любом случае, что меня смущает в приведенном выше коде, так это то, что программа не завершается. Я не ожидал, что это произойдет. Я немного прочитал о Executors.singleThreadExecutor
, и у меня есть идея, что если внутренний поток по какой-либо причине прекращает работу, он создает новый поток и безопасно «переносит» состояние во вновь созданный поток. Я понятия не имею, почему программа не завершается. Может кто-нибудь дать несколько советов?
Пожалуйста, обратите внимание, что приведенный здесь код не будет использоваться в производственных средах. Я написал это просто для практики.
Ответ №1:
Вы смешиваете два шаблона.
Если вы используете executor
s, вам не нужно присоединяться. Этот поток был запущен системным потоком, а не вашим основным потоком. Это не ваш дочерний поток, на самом деле вы не можете join
использовать его. Просто запустите и забудьте.
Если вы создаете поток самостоятельно, а затем запускаете его, вы должны join
это. Тогда дочерний поток ваш.
Комментарии:
1. Спасибо. Удаление
innerThread
переменной и вызовshutdown
onexecutor
после вызоваasyncGet
в main, похоже, решает проблему.
Ответ №2:
Ваша проблема здесь в том, что поток в вашей службе исполнителя не является потоком демона. Если вы укажете ThreadFactory
при создании своего исполнителя, то это должно сработать.
Executors.newFixedThreadPool(1,
new ThreadFactory() {
public Thread newThread(Runnable r) {
Thread t = Executors.defaultThreadFactory.newThread(r);
t.setDaemon(true);
return t;
}
});
Это приведет к созданию службы-исполнителя с потоками демона.
У вас также есть небольшое состояние гонки с вашим «sleep».
Вместо join
используйте CountdownLatch
.
Комментарии:
1. ДА… Я осведомлен о возможном состоянии гонки, но поскольку программа достаточно мала, и я получу исключение NullPointerException, если это произойдет, я решил перевести поток в спящий режим на некоторое время. В любом случае, спасибо. Мне придется посмотреть, что такое потоки демона 🙂