Интерфейс Runnable

java.lang.Runnable — функциональный интерфейс с единственным методом run(). Он представляет задачу, которую можно выполнить в любом потоке. В отличие от подкласса Thread, Runnable отделяет «что выполнять» от «где выполнять», и это считается более гибким подходом.

В реальных проектах AlashEd Runnable удобен, когда одну и ту же задачу надо выполнить и в основном потоке (например, для отладки), и в фоновом, и потом передать в ExecutorService. Поскольку Java 8 Runnable — функциональный интерфейс, его можно записать как лямбду или ссылку на метод.

Синтаксис

Runnable task = () -> {
    // работа задачи
};

// запуск в новом потоке
new Thread(task).start();

Пример 1. Runnable как отдельный класс

public class PrintTask implements Runnable {
    private final String label;

    public PrintTask(String label) {
        this.label = label;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 3; i++) {
            System.out.println(label + " " + i);
        }
    }

    public static void main(String[] args) {
        Runnable r = new PrintTask("sensor");
        new Thread(r).start();
    }
}

Output:

sensor 1
sensor 2
sensor 3

Пример 2. Лямбда-выражение

public class LambdaRunnable {
    public static void main(String[] args) throws InterruptedException {
        Runnable task = () -> {
            for (int i = 1; i <= 3; i++) {
                System.out.println("tick " + i);
            }
        };

        Thread t = new Thread(task, "Worker-1");
        t.start();
        t.join();
        System.out.println("готово");
    }
}

Output:

tick 1
tick 2
tick 3
готово

Пример 3. Два потока с разными задачами

public class TwoRunnables {
    public static void main(String[] args) {
        Runnable motor = () -> {
            for (int i = 0; i < 3; i++) {
                System.out.println("motor работает");
                sleep(150);
            }
        };

        Runnable sensor = () -> {
            for (int i = 0; i < 3; i++) {
                System.out.println("sensor читает данные");
                sleep(100);
            }
        };

        new Thread(motor).start();
        new Thread(sensor).start();
    }

    private static void sleep(long ms) {
        try { Thread.sleep(ms); } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

Output (порядок может меняться):

motor работает
sensor читает данные
sensor читает данные
motor работает
sensor читает данные
motor работает

Пример 4. Runnable + общий счётчик

public class CounterDemo {
    private static int counter = 0;

    public static void main(String[] args) throws InterruptedException {
        Runnable inc = () -> {
            for (int i = 0; i < 1000; i++) {
                counter++;
            }
        };

        Thread t1 = new Thread(inc);
        Thread t2 = new Thread(inc);
        t1.start();
        t2.start();
        t1.join();
        t2.join();

        System.out.println("counter = " + counter);
    }
}

Output (значение «прыгает», не всегда 2000 — пример race condition):

counter = 1873

Пример 5. Ссылка на метод

public class MethodRefRunnable {
    static void readSensor() {
        System.out.println("read: " + System.currentTimeMillis());
    }

    public static void main(String[] args) {
        Runnable r = MethodRefRunnable::readSensor;
        new Thread(r).start();
        new Thread(r).start();
    }
}

Output:

read: 1715774400123
read: 1715774400124

Подводные камни

Предупреждение

Runnable.run() не возвращает значение и не позволяет бросать проверяемые исключения. Если нужно получить результат — используйте Callable.

Предупреждение

Совместное изменение полей из нескольких Runnable без синхронизации приводит к race condition (см. Пример 4). Используйте synchronized, AtomicInteger или concurrent-коллекции.

Совет

Передавайте Runnable в ExecutorService, а не в new Thread(...). Пулы потоков переиспользуют ресурсы и проще управляются.

См. также

Примечание

Материал подготовлен на основе раздела «Concurrency» из Oracle Java Tutorials (docs.oracle.com/javase/tutorial/essential/concurrency/) и распространяется в соответствии с Oracle Free Documentation License. Тексты и примеры кода написаны заново на русском языке для AlashEd Wiki.