Poesía Binaria

Introducción a Timer y TimerTask en Java.

Los timers son una herramienta muy útil para lanzar una tarea cada cierto tiempo, por ejemplo, si estamos conectados a un servidor, podemos enviar información de estado para no desconectarnos, o lanzar una tarea a una hora determinada, o incluso puede servir para crear una animación (ya que con el tiempo va variando aquello que hayamos dibujado, o va cambiando el fotograma).
El ejemplo más sencillo de un timer puede ser lo siguiente:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.util.Timer;
import java.util.TimerTask;

class TimerEx {
    public static void main(String arglist[]) {
    Timer timer;
    timer = new Timer();

    TimerTask task = new TimerTask() {
        int tic=0;

        @Override
        public void run()
        {
            if (tic%2==0)
            System.out.println("TIC");
            else
            System.out.println("TOC");
            tic++;
        }
        };
        // Empezamos dentro de 10ms y luego lanzamos la tarea cada 1000ms
    timer.schedule(task, 10, 1000);
    }
}

La salida de este programa será un TIC / TOC alternativamente cada segundo por la consola, ya que la tarea la programamos cada 1000ms. Es extremadamente fácil, en principio tenemos que crear un objeto TimerTask que defina la tarea que vamos a ejecutar dentro de su método run() (el cuál tenemos que invalidar por uno nuestro (con @Override). Luego cargaremos un Timer, y con su método schedule llamaremos a la tarea, indicándole el tiempo que debe esperar para empezar y el tiempo tras el cuál repetirá la tarea de forma indefinida.

Si sólo indicamos un argument, la tarea se ejecutará una sola vez dentro de N milisegundos. También se pueden especificar objectos de tipo Date por argumentos.

Por otro lado, cabe mencionar que junto con schedule() tenemos otro método, scheduleAtFixedRate, la diferencia es que si ocurren retrasos en la ejecución de las tareas, por ejemplo, que esté nuestra aplicación haciendo otras cosas y en lugar de 1000ms, en una de las ejecuciones se tarden 1500, la tarea se ejecutará a los 500ms para compensar. Si el retraso es más grande, se ejecutará varias veces hasta recuperar el ritmo. schedule(), si se retrasa no hará nada, dentro de 1000ms volverá a ejecutar de nuevo la tarea.

Hemos visto que el TimerTask lo he creado de forma implícita, para un ejemplo, o para algo sencillo lo podemos hacer sin miedo. Si ya tenemos una tarea más grande y compleja, lo suyo sería crear una clase que extienda TimerTask para esa tarea, pero eso ya lo veremos más adelante.

Algo interesante que podemos hacer es llamar a una variable externa, es decir, desde nuestro TimerTask, llamar a un atributo de TimerEx, aunque no tiene nada que ver con cómo se hacen los Timers es algo curioso, que a lo mejor produce un pequeño calentamiento de cabeza.
Y es que a lo mejor queremos acceder a ese atributo, es decir, utilizar algo que está fuera del TimerTask

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import java.util.Timer;
import java.util.TimerTask;

class TimerEx {
    public static void main(String arglist[]) {
    Timer timer;
    timer = new Timer();
    final String str = new String("REACHED TEN TICS");

    TimerTask task = new TimerTask() {
        int tic = 0;

        @Override
        public void run()
        {
            if (tic%2==0)
            System.out.println("TIC");
            else
            System.out.println("TOC");
            tic++;
            if (tic%10==0)
            {
                System.out.println(str);
            }
        }
        };

    timer.schedule(task, 0, 1000);
    }
}

Con este ejemplo, cuando hayamos llamado a la tarea 10 veces, se imprimirá en pantalla la cadena str. Vamos, que desde estas clases anidadas o implícitas, podemos acceder a atributos declarados fuera de los mismos, aunque al ser final no podremos escribir en ellos. De todas formas, al ser objetos podemos crear una clase que sí nos permita modificar datos de la misma. Imaginemos si queremos sacar el atributo tic fuera del TimerTask, para ello debemos crear una clase nueva TicClass y modificar ligeramente el código de TimerEx:

TicClass.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class TicClass
{
    private int value;

    public TicClass(int initialValue)
    {
    this.value=initialValue;
    }

    public TicClass()
    {
    this.value=0;
    }

    public void setValue(int newValue)
    {
    this.value=newValue;
    }

    public int getValue()
    {
    return this.value;
    }

    public void increment()
    {
    this.value++;
    }
}

TimerEx.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.util.Timer;
import java.util.TimerTask;

class TimerEx {
    public static void main(String arglist[]) {
    Timer timer;
    timer = new Timer();
    final TicClass tic;
    tic = new TicClass(5);

    TimerTask task = new TimerTask() {
        @Override
        public void run()
        {
            if (tic.getValue()%2==0)
            System.out.println("TIC");
            else
            System.out.println("TOC");

            tic.increment();

            if (tic.getValue()%10==0)
            {
                System.out.println("TEN TICS!");
            }
        }
        };

    timer.schedule(task, 10, 1000);
    }
}

Con esta idea, podemos hacer una clase colección de varios objetos que queramos modificar durante el transcurso del TimerTask y pasarlo como variable final.

Foto: Andy Roberts (Flickr) CC-by

También podría interesarte....