在定期运行的线程中自发执行代码

时间:2017-05-29 16:04:16

标签: java multithreading

我有一个定期执行代码的线程,e。 G。每10秒钟。我想选择以自发的方式调用相同的代码,而不必等待10秒。但是自动和自发执行的代码必须永远不会同时运行,而是如果用户在调用相同的方法时按下执行按钮,它们应该按顺序运行。

有没有人知道一个好的模式,甚至是一个可以满足这种要求的课程?

首先想到的是使工作方法同步。但是在这种情况下,手动执行(例如按下按钮)被阻止,并且必须等到线程中的方法结束。有没有阻止的更好的方法?

示例:

public class Executor extends Thread {

    // endless loop, executes work method periodically with pause inbetween
    @Override
    public void run() {

        while( true) {

            work( "automatic");

            pause(10000);

        }

    }

    // Working method that's executed periodically or manually
    private synchronized void work( String text) {

        System.out.println( "Working " + text + " " + System.currentTimeMillis());

    }

    // helper method that pauses the thread
    private static void pause( long sleepMs) {
        try {

            Thread.sleep(sleepMs);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        // start automatic execution
        Executor executor = new Executor();
        executor.start();

        // pause a while
        pause(1000);

        // manual execution
        executor.work( "manual");

    }

}

编辑:我的要求的解决方案:

public class ScheduledExecutor {

    public static void main(String[] args) throws InterruptedException {

        ScheduledThreadPoolExecutor executor = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(1);
        executor.scheduleWithFixedDelay(new Work("auto"), 0, 10, TimeUnit.SECONDS);

        Thread.sleep(1000);

        executor.execute(new Work("manual"));

    }

    public static class Work implements Runnable {

        String text;

        public Work(String text) {
            this.text = text;
        }

        @Override
        public void run() {

            System.out.println("Working " + text + " " + System.currentTimeMillis());

        }
    }

}

3 个答案:

答案 0 :(得分:3)

  

我有一个定期执行代码的线程,e。 G。每10秒钟。我想选择以自发方式调用相同的代码,而不必等待10秒。

在代码中执行此操作的一种简单方法是,不要使用0x7F暂停,而是执行Thread.sleep(...)。然后,只要您希望命令唤醒并手动运行,它就会执行wait(...)

所以你的代码看起来像:

notify()

然后,如果您想让它手动运行,请执行以下操作:

while( true) {
    work( "automatic");
    synchronized (this) {
       try {
           // wait for a bit but allow someone else to awake us to run manually
           wait(10000);
       } catch (InterruptedException ie) {
           // always a good pattern
           Thread.currentThread().interrupt();
           return;
       }
    }
}

通知会立即唤醒线程,以便它可以运行它的任务。然后,工作方法不需要synchronized (executor) { executor.notify(); } ,因为只有synchronized线程正在运行它。

注意:正如@shinobi所指出的那样,使用Executor这样可能会受到虚假唤醒的影响,这可能会在某些操作系统线程实现时发生。

最后,制作Executor implement Runnable as opposed to extending Thread是一种更好的做法。

答案 1 :(得分:3)

我会创建一个新的单线程执行程序服务:

ExecutorService executorService = Executors.newFixedThreadPool(1);

然后,我会设置一个计时器,每隔10秒向executorService任务提供一次。

new Timer(10000, new ActionListener {
    public void actionPerformed(ActionEvent evt) {
        executorService.execute(() -> doWhatever());
    }
}).start();

最后,您可以在按钮按下处理程序中调用executorService.execute(() -> doWhatever());,或在代码中的任何其他位置调用。

一次只能激活一次doWhatever(),因为executorService只有一个线程可以运行它们。并且,按钮按下处理程序将永远不必等待,因为它只会在队列中放置新对象。

答案 2 :(得分:0)

在服务器线程(执行任务的线程)和客户端线程(需要触发立即执行的线程)之间共享信号量:

    Semaphore sem = new Semaphore( 0 );

服务器线程需要执行以下代码(请注意,它是一个无限循环 - 您可能希望将程序终止检查作为while()的条件插入):

    while( true ) {
        try {
            sem.tryAcquire( 10, TimeUnit.SECONDS );
        } catch( InterruptedException e ) {
            continue;
        }

        runTask();
        sem.drainPermits();
    }

然后,为了触发立即执行,客户端线程需要执行:

    sem.release();

因此,只要客户端线程在Semaphore.tryAcquire()中释放一个(触发立即执行)或超时,就会从信号量获取许可时执行任务(定期执行10s, end-to-start 。)相隔10秒执行 start-to-start 将采用一些稍微复杂的逻辑,并跟踪上次执行的情况。开始时间,但基本想法保持不变。

每次都需要排空许可证,以避免多次背靠背执行任务,如果在执行过程中可能会被触发立即执行。