Skip to content

5 Busy Waiting

Espera activa (busy-waiting)

En programación, se denomina espera activa o espera ocupada a una técnica donde un hilo o proceso repetidamente verifica una condición, tal como esperar una entrada de teclado o si se da una determinada condición, en espera de que ésta se de en algún momento futuro debido a la acción de algún otro hilo.

Como norma general, debe ser evitada, ya que consume tiempo de CPU sin realizar ninguna operación, ya que el hilo que se encuentra en la espera activa no cede el procesador.

Info

Con el método Thread.onSpinWait() de Java 9 indicamos al sistema que estamos en espera activa

El método estático Thread.onSpinWait(), añadido en Java 9, permite indicar al entorno de ejecución que el hilo momentáneamente no es capaz de progresar, hasta que ocurra alguna condición que se está comprobando en espera activa. Como consecuencia, el entorno de ejecución puede llevar a cabo determinadas acciones para mejorar el rendimiento del sistema, como asignar más recursos a otros hilos. De esta manera se consigue retrasar la ejecución de la siguiente instrucción del hilo en espera activa durante un determinado número de ciclos de procesador, con la esperanza de que la ejecución de otros hilos hagan que la condición que lo bloquea deje de producirse y el hilo pueda avanzar, minimizando el consumo de energía. El número de ciclos de retraso puede variar de una familia de procesadores a otra.

En este caso el uso de Thread.onSpinWait() es más efectivo que el de Thread.sleep(milliseconds), en el que se obliga al hilo a abandonar el procesador durante el tiempo indicado, obligando a un cambio de contexto adicional, que conlleva un coste adicional de tiempo.

Proyecto BusyWaiting

En este proyecto crearemos una aplicación para simular el inicio de una carrera de fórmula 1. Así, el hilo principal creará un hilo director de carrera y 10 hilos cada uno de los cuales represente un piloto que participa en la carrera. Además el hilo principal creará un objeto compartido que representa el sistema de luces en forma de semáforos que se usa para dar inicio a la carrera, que inicialmente tiene el color rojo. Los pilotos calientan motores en espera de que el sistema de luces esté en verde. Transcurridos tres segundos, el director de carrera pone el sistema de luces en amarillo y transcurridos otros tres segundos lo pone en verde. Los pilotos, que se encuentran en una espera activa hasta que el sistema de luces se ponga en verde, en cuento detectan que dicho evento se ha producido, inician la carrera.

class Main {

    private static final int NUMBER_OF_PILOTS = 10;

    public static void main(String[] args) {
        LightSystem lightSystem = new LightSystem();
        Thread raceDirectorThread = new Thread(new RaceDirector(lightSystem), 
                                            "Race director");
        Thread[] pilots = new Thread[NUMBER_OF_PILOTS];
        for (int i = 0; i < NUMBER_OF_PILOTS; i++) {
            pilots[i] = new Thread(new Pilot(i, lightSystem), "Pilot " + i);
        }
        raceDirectorThread.start();
        for (int i = 0; i < NUMBER_OF_PILOTS; i++) {
            pilots[i].start();
        }
    }

}
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

class LightSystem {

    public enum Color {
        RED, YELLOW, GREEN
    }

    private final DateTimeFormatter dateTimeFormatter = 
        DateTimeFormatter.ofPattern("HH:mm:ss");

    private volatile Color currentColor = Color.RED;

    void setColor(Color newColor) {
        currentColor = newColor;
        System.out.printf("%s - Light system in %s\n", 
                        LocalDateTime.now().format(dateTimeFormatter), newColor);
    }

    Color getColor() {
        return currentColor;
    }

}
import java.util.Objects;

public class RaceDirector implements Runnable {

    private final LightSystem lightSystem;

    public RaceDirector(LightSystem lightSystem) {
        Objects.requireNonNull(lightSystem);
        this.lightSystem = lightSystem;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(3000);
            lightSystem.setColor(LightSystem.Color.YELLOW);
            Thread.sleep(3000);
            lightSystem.setColor(LightSystem.Color.GREEN);
        } catch (InterruptedException e) {
            return;
        }
    }

}
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Objects;

class Pilot implements Runnable {

    private final int number;
    private final LightSystem lightSystem;

    private final DateTimeFormatter dateTimeFormatter = 
        DateTimeFormatter.ofPattern("HH:mm:ss");

    Pilot(int number, LightSystem lightSystem) {
        Objects.requireNonNull(lightSystem);
        this.number = number;
        this.lightSystem = lightSystem;
    }

    @Override
    public void run() {
        warmup();
        while (!Thread.currentThread().isInterrupted() && 
            lightSystem.getColor() != LightSystem.Color.GREEN) {
            Thread.onSpinWait();
        }
        if (!Thread.currentThread().isInterrupted()) {
            start();
        }
    }

    private void start() {
        System.out.printf("%s - Pilot %d starts the race!!!\n", 
                        LocalDateTime.now().format(dateTimeFormatter), number);
    }

    private void warmup() {
        System.out.printf("%s - Pilot %d is warming up car engine\n", 
                        LocalDateTime.now().format(dateTimeFormatter), number);
    }

}