Интерфейс 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.