【问题标题】:Spontaneous execution of code in periodically running thread在周期性运行的线程中自发执行代码
【发布时间】:2017-10-30 00:05:40
【问题描述】:

我有一个定期执行代码的线程,例如。 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());

        }
    }

}

【问题讨论】:

  • 中断睡眠?
  • 如果线程在不处于睡眠模式时被中断会发生什么?
  • 您能否更新您的问题以明确您希望它如何工作。您是说您不希望手动和自动的线程代码同时运行。然后你说你不希望手动执行在你按下按钮时被阻止......
  • 强调Allan的一点:如果任务在定时器上运行,并且按下了按钮,你会1)中止正在运行的定时器启动的执行,并立即执行按钮启动的任务,或者 2) 完全跳过按钮启动的任务,或者 3) 阻塞,等待定时器启动的执行完成,然后运行按钮启动的任务?
  • 完成。这是选项3,我。 e.等待线程完成,然后运行手动启动的任务。

标签: java multithreading


【解决方案1】:

我将创建一个新的单线程执行器服务:

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 只有一个线程可以运行它们。而且,您的按钮按下处理程序将永远不必等待,因为它只会将一个新对象放入队列中。

【讨论】:

  • 谢谢!这让我走向了正确的方向。而不是使用ScheduledThreadPoolExecutor 的计时器并使用scheduleWithFixedDelay 调用工作方法更符合我的要求。
【解决方案2】:

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

在您的代码中执行此操作的一种简单方法是不要使用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,因为只有Executor 线程在运行它。

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

最后,最好是Executor implement Runnable as opposed to extending Thread。

【讨论】:

【解决方案3】:

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

    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() 中超时(定期执行相隔 10 秒,end-to-start.) 间隔 10 秒执行 start-to-start 需要一些稍微复杂的逻辑,以及跟踪上次执行的开始时间,但基本的想法保持不变。

您需要每次都耗尽许可,以避免任务的多次背靠背执行,以防它可能被触发立即执行而仍在执行。

【讨论】:

    猜你喜欢
    • 2017-05-18
    • 2021-10-16
    • 2012-09-10
    • 2022-11-11
    • 1970-01-01
    • 1970-01-01
    • 2015-01-25
    • 2011-06-17
    • 1970-01-01
    相关资源
    最近更新 更多