DEV Community

faangmaster
faangmaster

Posted on • Edited on

Как выполнить код в отдельном потоке в Java?

В Java существует несколько способов выполнить код/задачу в отдельном потоке.

Наследование класса Thread

Первый способ - это создать свой класс, который наследуется от класса Thread.
Например,

public class MyThread extends Thread { public void run() { ..... } } 
Enter fullscreen mode Exit fullscreen mode

Для запуска нашего потока нужно создать инстанс класса и вызвать метод start():

MyThread myThread = new MyThread(); myTread.start(); 
Enter fullscreen mode Exit fullscreen mode

Также можно создать анонимный класс, наследующий класс Thread:

Thread myThread = new Thread() { public void run() { ..... } } myThread.start(); 
Enter fullscreen mode Exit fullscreen mode

Имплиментировать интерфейс Runnable

Для этого нужно объявить класс, который реализует интерфейс Runnablе. Далее создать инстанс этого класса и передать его в конструктор объекта класса Thread и вызвать метод start():

public class MyRunnable implements Runnable { public void run() { ..... } } .... Runnable myRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable); thread.start(); 
Enter fullscreen mode Exit fullscreen mode

Аналогично, вместо явного объявления класса, который реализует интерфейс Runnable, можно создать анонимный класс:

Runnable myRunnable = new Runnable() { public void run(){ ..... } }; ... Thread thread = new Thread(myRunnable); thread.start(); 
Enter fullscreen mode Exit fullscreen mode

Или используя lambda:

Runnable myRunnable = () -> {.....}; ... Thread thread = new Thread(myRunnable); thread.start(); 
Enter fullscreen mode Exit fullscreen mode

Использование Executor

Вместо явного создания потока, задачу можно выполнить используя Executor framework. Для этого нужно создать Thread Pool:

Executor executor = Executors.newCachedThreadPool(); 
Enter fullscreen mode Exit fullscreen mode

И вызвать метод execute, в который нужно передать наш Runnable:

Runnable myRunnable = new Runnable() { public void run() { ..... } }; executor.execute(myRunnable); 
Enter fullscreen mode Exit fullscreen mode

Существует четыре основных Thread Pool, которые можно использовать:

newFixedThreadPool - создает новые потоки, по мере сабмита тасок, вплоть до максимального размера пула. Далее поддерживает размер пула постоянным. Если поток упадет из-за unexpected Exception, то создаст новый поток.
newCachedThreadPool - Если потоки не используются(idle), может их убивать. Если же число задач увеличивается, то создает новые потоки. При этом не имеет верхнего предела по числу потоков.
newSingleThreadExcutor - создает всего один поток. Если он падает, то создает новый. Гарантирует выполнение задач последовательно.
newScheduledThreadPool - Пул фиксированного размера. Поддерживает выполение отложенных и периодических задач по рассписанию.

Callable, Future и ExecutorService

Как вы успели заметить, Runnable имеет один метод - run, который не возращает никакого результата (void). Если нам надо, чтобы наша задача/код, выполняемая в отдельном потоке, вернула какой-то результат - мы можем использовать Callable.
Объявим класс (анонимный), который реализует Callable:

Callable myCallable = new Callable<List<String>>() { public List<String> call() throws Exception { ........ return result; } }; 
Enter fullscreen mode Exit fullscreen mode

Создадим ExecutorService и вызовем метод submit, вместо execute:

ExecutorService executor = Executors.newCachedThreadPool(); Future<List<String>> future = executor.submit(myCallable); try { List<String> result = future.get(); } catch (InterruptedException e) { .... } catch (ExecutionException e) { .... } 
Enter fullscreen mode Exit fullscreen mode

Метод submit вернет в качестве результата Future. Для получения результата, нужно вызвать метод get. Этот метод блокирующий, вызывающий поток будет ожидать, потока результат станет доступным.

CompletionService

Если мы хотим выполнить множество задач, и результаты получать в порядке их доступности, то можно использовать CompletionService.
Для этого нужно обернуть ExecutorService в ExecutorCompletionService:

ExecutorService executor = Executors.newCachedThreadPool(); CompletionService<List<String>> completionService = new ExecutorCompletionService<>(executor); for (....) { completionService.submit(myCallable); } for (...) { try { Future<List<String>> future = completionService.take(); List<String> result = future.get(); } catch (InterruptedException e) { ..... } catch (ExecutionException e) { ...... } } 
Enter fullscreen mode Exit fullscreen mode

С будущих статьях также расскажу про CompletableFuture и виртуальные потоки.

Top comments (0)