← Назад к вопросам

Что возвращает метод run() из интерфейса Runnable?

2.0 Middle🔥 111 комментариев
#Stream API и функциональное программирование#Многопоточность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что возвращает метод run() из Runnable

Это фундаментальный вопрос о многопоточности в Java. run() из Runnable возвращает void — абсолютно ничего. Разберу почему и как это использовать.

Определение интерфейса Runnable

public interface Runnable {
    /**
     * Когда объект реализует интерфейс Runnable,
     * поток, выполняющий объект, вызовет метод run()
     */
    void run();
}

void — буквально ничего не возвращается.

Примеры use

Пример 1: Простой Runnable

public class SimpleRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Task executed");
        // НИЧЕГО не возвращаем
    }
}

// Использование
Runnable task = new SimpleRunnable();
Thread thread = new Thread(task);
thread.start();
// thread.run() вернёт void

Пример 2: Lambda выражение

Runnable task = () -> {
    System.out.println("Task executed");
    // void — ничего не возвращаем
};

Thread thread = new Thread(task);
thread.start();
// Вернёт void

Пример 3: Попытка вернуть значение — ОШИБКА

// ОШИБКА КОМПИЛЯЦИИ
Runnable task = () -> {
    return 42;  // ОШИБКА: Runnable.run() возвращает void
};

// ИСПРАВЛЕНИЕ: используй Callable<T>
Callable<Integer> task = () -> {
    return 42;  // OK
};

Runnable vs Callable

Runnable — void, результата нет:

public interface Runnable {
    void run();  // Ничего не возвращает
}

// Использование
Thread thread = new Thread(() -> {
    System.out.println("No result");
});
thread.start();
// Нет способа получить результат

Callable<T> — возвращает T:

public interface Callable<T> {
    T call() throws Exception;  // Возвращает T
}

// Использование
Callable<Integer> callable = () -> {
    System.out.println("With result");
    return 42;
};

ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(callable);
Integer result = future.get();  // 42

Таблица сравнения

┌─────────────┬────────────────────┬──────────────────┐
│ Характеристика │ Runnable        │ Callable<T>      │
├─────────────┼────────────────────┼──────────────────┤
│ Возвращаемое │ void             │ T                │
│ Проверяемые исключения │ нет    │ да               │
│ execute()   │ ✅ можно          │ ✗ нет            │
│ submit()    │ ✅ можно          │ ✅ лучше         │
│ Thread      │ ✅ можно          │ ✗ нет            │
│ Future      │ Future<?> (null)  │ Future<T>        │
└─────────────┴────────────────────┴──────────────────┘

Практические примеры

Пример: Обработка списка файлов

// Runnable — обрабатываем, но результата нет
ExecutorService executor = Executors.newFixedThreadPool(3);

List<String> files = Arrays.asList("file1.txt", "file2.txt");

for (String file : files) {
    executor.execute(() -> {
        // Runnable.run() возвращает void
        processFile(file);
        System.out.println("Processed " + file);
        // НИЧЕГО не возвращаем
    });
}

private void processFile(String file) {
    System.out.println("Processing " + file);
}

Если нужен результат — используй Callable:

ExecutorService executor = Executors.newFixedThreadPool(3);

List<String> files = Arrays.asList("file1.txt", "file2.txt");
List<Future<ProcessResult>> futures = new ArrayList<>();

for (String file : files) {
    Future<ProcessResult> future = executor.submit(() -> {
        // Callable.call() возвращает результат
        ProcessResult result = processFileAndReturn(file);
        return result;  // Возвращаем результат
    });
    futures.add(future);
}

// Собираем результаты
for (Future<ProcessResult> future : futures) {
    ProcessResult result = future.get();
    System.out.println(result);
}

private ProcessResult processFileAndReturn(String file) {
    return new ProcessResult(file, "processed");
}

Исключения в Runnable vs Callable

Runnable — не может выбросить проверяемое исключение:

// ОШИБКА КОМПИЛЯЦИИ
Runnable task = () -> {
    Files.readAllLines(Paths.get("file.txt"));
    // IOException не обработано!
    // Runnable.run() не выбрасывает IOException
};

// ИСПРАВЛЕНИЕ: обработать исключение
Runnable task = () -> {
    try {
        Files.readAllLines(Paths.get("file.txt"));
    } catch (IOException e) {
        System.out.println("Error: " + e);
    }
};

Callable — может выбросить проверяемое исключение:

// OK
Callable<List<String>> task = () -> {
    return Files.readAllLines(Paths.get("file.txt"));
    // IOException может быть выброшено
};

ExecutorService executor = Executors.newSingleThreadExecutor();
try {
    List<String> lines = executor.submit(task).get();
} catch (ExecutionException e) {
    System.out.println("Error: " + e.getCause());
}

Почему Runnable возвращает void?

Исторический контекст:

Runnable был создан в Java 1.0 (1996) для потокования. В то время многопоточность была проще — потоки просто выполняли задачу и завершались.

Callable добавлен позже (Java 1.5, 2004) с ExecutorService для поддержки результатов и обработки исключений.

Получение результата из Runnable

Если абсолютно нужен результат и ты вынужден использовать Runnable:

Способ 1: Shared переменная

class Result {
    int value;
}

Result result = new Result();

Runnable task = () -> {
    result.value = calculateSomething();
    // Записали результат в shared объект
};

Thread thread = new Thread(task);
thread.start();
thread.join();  // Ждём завершения

System.out.println("Result: " + result.value);

Способ 2: Callback

functional interface Callback<T> {
    void callback(T result);
}

Runnable task = () -> {
    int value = calculateSomething();
    callback.callback(value);  // Вызываем callback
};

Callback<Integer> callback = (value) -> {
    System.out.println("Result: " + value);
};

Способ 3: Просто используй Callable (ЛУЧШЕ):

Callable<Integer> task = () -> {
    return calculateSomething();  // Возвращаем результат
};

Future<Integer> future = executor.submit(task);
int value = future.get();
System.out.println("Result: " + value);

Best Practices

Используй Runnable когда:

  • Не нужен результат
  • Fire and forget операции
  • Фоновые задачи
executor.execute(() -> {
    logger.info("Background task");
    cleanup();
});

Используй Callable когда:

  • Нужен результат
  • Может быть исключение
  • Нужно дождаться завершения
Future<User> future = executor.submit(() -> {
    return database.getUser(id);
});

User user = future.get();  // Получаем результат

Итог

Метод run() из Runnable возвращает void:

public void run() {
    // Выполняем работу
    // НИЧЕГО не возвращаем
}

Основные моменты:

  • void — буквально ничего не возвращается
  • Используй Callable<T> если нужен результат
  • Используй Runnable для fire-and-forget задач
  • Difference между ними критична для выбора правильного инструмента
Что возвращает метод run() из интерфейса Runnable? | PrepBro