【问题标题】:Synchronize two timers with multiple instances (Java)使用多个实例同步两个计时器(Java)
【发布时间】:2012-04-17 17:11:01
【问题描述】:

我是 Java 新手,过去没有使用过线程。现在我的知识水平遇到了一个非常棘手的问题。

我开发了“发送邮件”和“检查邮件”两个功能。每个用户注册他的偏好,系统会创建两个目录,其中包含许多用于定期工作的指令,例如

A) 每 1500 毫秒发送一次消息

B) 每 1800 毫秒发送一次消息

C) 每 3000 毫秒发送一次消息

并且 A) 每 2000 毫秒检查一次消息

B) 每 6000 毫秒检查一次消息

C) 每 8000 毫秒检查一次消息

我用线程尝试了很多方法,但我没有成功。 我的问题是同步它们的最佳方法是什么?下面是我最后一次使用线程的代码。

public class MailCreatorThread extends Thread {
    @Override
    public void run() {
        CreateMail(_date); //creates a mail with _date as subject
    }
}

public class GPSThread extends Thread {
    @Override
    public void run() {
        // TODO Auto-generated method stub
        while (!_isTimeToStop) {
            try {
                while (_servicesToUpdate.size() == 0) {
                    Thread.sleep(500);
                }
                _currentService = (MyService) _servicesToUpdate.get(0)
                        .clone();
                _servicesToUpdate.remove(0);

                        MailCreatorThread mailCreatorThread = new MailCreatorThread();
                        mailCreatorThread.start();


            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

public class CheckServicesThread extends Thread {
    @Override
    public void run() {
        // TODO Auto-generated method stub
        while (!_isTimeToStop) {
            try {
                Thread.sleep(1000);
                    for (int j = 0; j < _servicesList.length; j++) {
                        MyService currentService = ((MyService) _servicesList[j]);
                        if (myService.getTimeToNextUpdate() - 1000 <= 0) {
                            _servicesToUpdate
                                    .add((MyService) currentService
                                            .clone());
                            currentService
                                    .setTimeToNextUpdate(currentService
                                            .getUpdatePeriod());
                        } else {
                            currentService
                                    .setTimeToNextUpdate(currentService
                                            .getTimeToNextUpdate() - 1000);
                        }
                }

            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        stopSelf();
    }
}

【问题讨论】:

    标签: java multithreading sync


    【解决方案1】:

    您可以使用ScheduledExecutorService 来运行这些定期任务。语法相当简单:

    Runnable check = new Runnable() {
        public void run() {
            checkMessage();
        }
    }
    Runnable send = new Runnable() {
        public void run() {
            sendMessage();
        }
    }
    
    //since what you are doing is mostly I/O you probably want to have 
    //more than one thread available so that if one operation blocks,
    //the other ones can be launched in parallel
    
    ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(10);
    scheduler.scheduleAtFixedRate(check, 0, 1500, MILLISECONDS);
    scheduler.scheduleAtFixedRate(send, 0, 6000, MILLISECONDS);
    

    注意:Ozzy's answer 中提到的 Timer 不应再使用,因为它已在 Java 1.5 中由 ScheduledThreadPoolExecutor 改进,如 Timer's javadoc 中所述:

    Java 5.0 引入了 java.util.concurrent 包,其中的并发实用程序之一是 ScheduledThreadPoolExecutor,它是一个线程池,用于以给定的速率或延迟重复执行任务。它实际上是 Timer/TimerTask 组合的更通用替代品,因为它允许多个服务线程,接受各种时间单位,并且不需要子类化 TimerTask(只需实现 Runnable)。使用一个线程配置 ScheduledThreadPoolExecutor 使其等效于 Timer。

    【讨论】:

      【解决方案2】:

      在 java 中,你有内置的 Timer 和 TimerTask 类来帮助你在一个单独的线程中重复一个任务。

      这将创建一个 Timer,它将创建自己的后台线程:

      Timer t = new Timer();
      

      然后,您可以为该计时器安排任意数量的任务,它们将共享计时器自己的线程。

      这是在 2000 毫秒 = 2 秒的延迟之后在计时器线程上调度单个任务的方法:

      t.schedule(new TimerTask() {
          @Override
          public void run() {
              //task to perform
          }
      }, 2000);
      

      这是您可以在计时器线程上安排重复任务的方法,延迟 1 秒后,并以 1.5 秒的间隔重复该任务:

      t.scheduleAtFixedRate(new TimerTask() {
          @Override
          public void run() {
              //task to perform every 1.5 seconds
          }
      }, 1000, 1500);
      

      现在您可以选择将两个任务(checkMail、sendMail)安排到同一个 Timer(同一个线程)或给它们各自的 Timer(单独的线程)。

      有关更多信息,请参阅 java 文档 (http://docs.oracle.com/javase/6/docs/api/java/util/Timer.html)

      希望这会有所帮助。

      【讨论】:

      • 对于 Java 1.5+,您应该建议使用 ScheduledThreadPoolExecutor 而不是 Timer - 请参阅我的答案。
      • Ozzy 谢谢您的宝贵时间!我尝试了您的解决方案,它的工作原理与您描述的完全一样,但是在 java 中进行了一些搜索后,我检索到 assylias 说 ScheduledExecutorService 是推荐的工具。
      猜你喜欢
      • 2012-05-21
      • 2012-01-31
      • 2017-11-27
      • 1970-01-01
      • 2019-11-30
      • 2019-03-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多