2017-05-29 199 views
1

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

     } 
    } 

} 
+0

中断睡眠? –

+0

如果线程在非休眠模式下被中断会发生什么? – Roland

+0

您是否可以更新您的问题以清楚说明您希望如何工作。你说你不希望手动和自动的线程代码同时运行。然后你说你不想在按下按钮时手动执行被阻止...... – Allan

回答

3

我会创造一个新的,单独的线程执行服务:

ExecutorService executorService = Executors.newFixedThreadPool(1); 

然后,我会成立哺养executorService任务每10秒一次的计时器。

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

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

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

+1

谢谢!这让我走向了正确的方向。而不是使用'ScheduledThreadPoolExecutor'并使用'scheduleWithFixedDelay'调用工作方法的Timer与我的需求更好地匹配。 – Roland

3

我有其周期性地执行代码的线程,即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(); 
} 

的通知将立即唤醒线程,以便它可以运行这是任务。那么工作方法不需要是​​,因为只有Executor线程正在运行它。

注:正如指出的@shinobi,使用wait()这样可以从虚假的唤醒,可与某些操作系统线程实现发生受苦。

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

+0

精美简约,但不防范虚假唤醒(https://stackoverflow.com/questions/13148001/spurious-wakeups-wait-and-notifyall)。 – shinobi

+0

好点。猜猜这取决于是否额外运行是一个问题@shinobi。 – Gray

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()(定期执行的10秒后要么取得从信号量许可证执行任务从头开始。)执行间隔10s 开始启动将采取一些稍微更多的涉及逻辑,以及跟踪上次执行的开始时间,但基本思路保持不变。

为了避免多次背靠背执行任务,每次都需要排空许可证,以防在执行过程中立即执行时触发执行。