java: ejecuta una función después de un número específico de segundos

148

Tengo una función específica que quiero ejecutar después de 5 segundos. ¿Cómo puedo hacer eso en Java?

Encontré javax.swing.timer, pero realmente no puedo entender cómo usarlo. Parece que estoy buscando algo mucho más simple de lo que ofrece esta clase.

Agregue un ejemplo de uso simple.

ufk
fuente
¿Desea esperar 5 segundos y luego ejecutar algo o desea continuar haciendo otra cosa en los 5 segundos?
whiskeysierra
Quiero seguir haciendo otra cosa
ufk

Respuestas:

229
new java.util.Timer().schedule( 
        new java.util.TimerTask() {
            @Override
            public void run() {
                // your code here
            }
        }, 
        5000 
);

EDITAR:

javadoc dice:

Después de que la última referencia en vivo a un objeto Timer desaparece y todas las tareas pendientes han completado la ejecución, el subproceso de ejecución de tareas del temporizador finaliza con gracia (y queda sujeto a la recolección de basura). Sin embargo, esto puede tomar arbitrariamente mucho tiempo para ocurrir.

tangenos
fuente
1
Si ejecuta ese código, perderá hilos. Asegúrate de limpiar el temporizador cuando hayas terminado.
skaffman
1
@skaffman: agregué una declaración del javadoc. ¿Realmente tienes que limpiar después del horario de llamadas?
tangens
1
Puede estar bien, pero puede que no lo esté. Si ejecuta ese fragmento de código varias veces, tendrá hilos sueltos dando vueltas sin ningún medio de ordenarlos.
skaffman
55
import java.util.Timer; import java.util.TimerTask;podría hacer más obvio que esto no es así javax.swing.Timer. / Tenga en cuenta que si está utilizando Swing (y en realidad AWT) no debería hacer nada para cambiar los componentes en los subprocesos de subproceso de subproceso (EDT) que no son eventos ( java.util.Timertareas malas; javax.swing.Timeracciones buenas).
Tom Hawtin - tackline
2
@PaulAlexander De acuerdo con los documentos, llamar al cancelmétodo del temporizador al final del método de ejecución eliminaría el hilo de ejecución de TimerTasks.
Dandalf
58

Algo como esto:

// When your program starts up
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();

// then, when you want to schedule a task
Runnable task = ....    
executor.schedule(task, 5, TimeUnit.SECONDS);

// and finally, when your program wants to exit
executor.shutdown();

Hay varios otros métodos de fábrica en los Executorque puede utilizar en su lugar, si desea más subprocesos en el grupo.

Y recuerde, es importante cerrar el ejecutor cuando haya terminado. El shutdown()método cerrará limpiamente el grupo de subprocesos cuando se haya completado la última tarea y se bloqueará hasta que esto suceda. shutdownNow()terminará el grupo de subprocesos inmediatamente.

skaffman
fuente
24

Ejemplo de uso javax.swing.Timer

Timer timer = new Timer(3000, new ActionListener() {
  @Override
  public void actionPerformed(ActionEvent arg0) {
    // Code to be executed
  }
});
timer.setRepeats(false); // Only execute once
timer.start(); // Go go go!

Este código solo se ejecutará una vez y la ejecución se realizará en 3000 ms (3 segundos).

Como menciona camickr, debe buscar " Cómo usar los temporizadores de oscilación " para una breve introducción.

zpon
fuente
6

Mi código es el siguiente:

new java.util.Timer().schedule(

    new java.util.TimerTask() {
        @Override
        public void run() {
            // your code here, and if you have to refresh UI put this code: 
           runOnUiThread(new   Runnable() {
                  public void run() {
                            //your code

                        }
                   });
        }
    }, 
    5000 
);
usuario2885850
fuente
5

Como una variación de @tangens, responda: si no puede esperar a que el recolector de basura limpie su hilo, cancele el temporizador al final de su método de ejecución.

Timer t = new java.util.Timer();
t.schedule( 
        new java.util.TimerTask() {
            @Override
            public void run() {
                // your code here
                // close the thread
                t.cancel();
            }
        }, 
        5000 
);
Dandalf
fuente
¿No Timer tdebería declararse finalya que se accede a él dentro de una clase interna?
Joshua Pinter
1
@JoshuaPinter Sí, debe declararse final, pero no necesita declararse explícitamente como final en al menos Java 8. Solo necesita ser "efectivamente final" ( javarevisited.blogspot.com/2015/03/… )
Dandalf
4

Su pregunta original menciona el "Temporizador de oscilación". Si, de hecho, su pregunta está relacionada con SWing, entonces debería usar el Swing Timer y NO el util.Timer.

Lea la sección del tutorial de Swing sobre " Cómo usar temporizadores " para obtener más información.

camickr
fuente
4

podrías usar la función Thread.Sleep ()

Thread.sleep(4000);
myfunction();

Su función se ejecutará después de 4 segundos. Sin embargo, esto podría pausar todo el programa ...

valle
fuente
¡Y solo garantiza que la ejecución se ejecutará después de 4 segundos, lo que podría significar también después de 10 segundos!
questzen
2
Questzen, encontrará que todos los métodos aquí hacen eso. De hecho, incluso si está programando algo en el nivel del sistema operativo, generalmente solo puede garantizar un tiempo mínimo transcurrido antes de un evento.
Ethan
Esta no es la pregunta real
Inder R Singh
Solo tenía que darle un voto negativo a esto, en absoluto una respuesta a la pregunta en cuestión.
theMayer
OP dijo en un comentario "quiero seguir haciendo otra cosa"; este código obviamente no lo hace.
Abhijit Sarkar
3

ScheduledThreadPoolExecutor tiene esta habilidad, pero es bastante pesado.

Timer también tiene esta habilidad pero abre varios hilos incluso si se usa solo una vez.

Aquí hay una implementación simple con una prueba (firma cercana al Handler.postDelayed () de Android ):

public class JavaUtil {
    public static void postDelayed(final Runnable runnable, final long delayMillis) {
        final long requested = System.currentTimeMillis();
        new Thread(new Runnable() {
            @Override
            public void run() {
                // The while is just to ignore interruption.
                while (true) {
                    try {
                        long leftToSleep = requested + delayMillis - System.currentTimeMillis();
                        if (leftToSleep > 0) {
                            Thread.sleep(leftToSleep);
                        }
                        break;
                    } catch (InterruptedException ignored) {
                    }
                }
                runnable.run();
            }
        }).start();
    }
}

Prueba:

@Test
public void testRunsOnlyOnce() throws InterruptedException {
    long delay = 100;
    int num = 0;
    final AtomicInteger numAtomic = new AtomicInteger(num);
    JavaUtil.postDelayed(new Runnable() {
        @Override
        public void run() {
            numAtomic.incrementAndGet();
        }
    }, delay);
    Assert.assertEquals(num, numAtomic.get());
    Thread.sleep(delay + 10);
    Assert.assertEquals(num + 1, numAtomic.get());
    Thread.sleep(delay * 2);
    Assert.assertEquals(num + 1, numAtomic.get());
}
AlikElzin-kilaka
fuente
da un sueño de advertencia llamado en un bucle
shareef
El whilees solo ignorar la interrupción.
AlikElzin-kilaka
2

Todas las demás respuestas requieren ejecutar su código dentro de un nuevo hilo. En algunos casos de uso simple, es posible que desee esperar un poco y continuar la ejecución dentro del mismo hilo / flujo.

El siguiente código demuestra esa técnica. Tenga en cuenta que esto es similar a lo que hace java.util.Timer debajo del capó, pero más liviano.

import java.util.concurrent.TimeUnit;
public class DelaySample {
    public static void main(String[] args) {
       DelayUtil d = new DelayUtil();
       System.out.println("started:"+ new Date());
       d.delay(500);
       System.out.println("half second after:"+ new Date());
       d.delay(1, TimeUnit.MINUTES); 
       System.out.println("1 minute after:"+ new Date());
    }
}

Implementación DelayUtil

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class DelayUtil {
    /** 
    *  Delays the current thread execution. 
    *  The thread loses ownership of any monitors. 
    *  Quits immediately if the thread is interrupted
    *  
    * @param duration the time duration in milliseconds
    */
   public void delay(final long durationInMillis) {
      delay(durationInMillis, TimeUnit.MILLISECONDS);
   }

   /** 
    * @param duration the time duration in the given {@code sourceUnit}
    * @param unit
    */
    public void delay(final long duration, final TimeUnit unit) {
        long currentTime = System.currentTimeMillis();
        long deadline = currentTime+unit.toMillis(duration);
        ReentrantLock lock = new ReentrantLock();
        Condition waitCondition = lock.newCondition();

        while ((deadline-currentTime)>0) {
            try {
                lock.lockInterruptibly();    
                waitCondition.await(deadline-currentTime, TimeUnit.MILLISECONDS);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return;
            } finally {
                lock.unlock();
            }
            currentTime = System.currentTimeMillis();
        }
    }
}
Valchkou
fuente
2
public static Timer t;

public synchronized void startPollingTimer() {
        if (t == null) {
            TimerTask task = new TimerTask() {
                @Override
                public void run() {
                   //Do your work
                }
            };

            t = new Timer();
            t.scheduleAtFixedRate(task, 0, 1000);
        }
    }
Amol K
fuente
2
Si bien este código puede responder a la pregunta, proporcionar un contexto adicional con respecto a por qué y / o cómo este código responde a la pregunta mejora su valor a largo plazo.
Mateus