DEV Community

faangmaster
faangmaster

Posted on • Edited on

1

Как выполнить код в отдельном потоке в 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 и виртуальные потоки.

Heroku

Build apps, not infrastructure.

Dealing with servers, hardware, and infrastructure can take up your valuable time. Discover the benefits of Heroku, the PaaS of choice for developers since 2007.

Visit Site

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay