精华内容
下载资源
问答
  • java里面定时器

    2019-03-17 02:03:14
    现在看来,不算一个很好的设计。 Timer timer = new Timer(); timer.schedule(new MyTimerTask(),1000,10); 其中MyTimerTask 是一个实现TimerTask的类,主要是实现run 方法。 Timer中有2个核心类,一个是Timer,...

    Timer

    JDK中的Timer 是一个非常早期的类,在1.2中就引入了。现在看来,不算一个很好的设计。

    Timer timer = new Timer();
    timer.schedule(new MyTimerTask(),1000,10);

    其中MyTimerTask 是一个实现TimerTask的类,主要是实现run 方法。

    Timer中有2个核心类,一个是Timer,一个TimerTask

    前者主要是定义定时任务什么时候执行,后者主要是定义具体定时任务。

    Timer

    public class Timer {
        private final TaskQueue queue = new TaskQueue();
        private final TimerThread thread = new TimerThread(queue);
    }

    其中TaskQueue 是使用数组实现的一个简易的堆,另外一个值得注意的属性是 TimerThread

    Timer使用唯一的线程负责轮询并执行任务。Timer的优点在于简单易用,但也因为所有任务都是由同一个线程来调度,因此整个过程是串行执行的,同一时间只能有一个任务在执行,所以在任务量多的情况下不方便使用。

    1.Timer 只能被单线程调度

    2.TimerTask 中出现的异常会影响到Timer 的执行

    由于这两点缺陷,JDK1.5 支持了新的定时器方案 ScheduledExecutorService。

    ScheduledExecutorService

    public static void main(String[] args) {
            ScheduledExecutorService scheduledExecutorService =
                    Executors.newScheduledThreadPool(10);
            scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
                    System.out.println("schedualed executor service");
                }
            },1, 1, TimeUnit.SECONDS);
        }

    相比Timer,ScheduledExecutorService 解决了同一个定时器调度多个任务的阻塞问题,并且任务异常不会中断 ScheduledExecutorService。

    ScheduledExecutorService 提供了2种常用的周期调度方法 ScheduleAtFixedRate 和 ScheduleWithFixedDelay。

    ScheduleAtFixedRate 基于固定时间间隔进行任务调度,ScheduleWithFixedDelay 取决于每次任务执行的时间长短,是基于不固定时间间隔的任务调度。

    ScheduledExecutorService 底层使用的数据结构为 PriorityQueue。

     

    展开全文
  • Java课程设计--定时器1.团队课程设计博客链接2.个人负责模块或任务说明框架构建时间设置面板,倒计时面板按钮设置3.自己的代码提交记录截图4.自己负责模块或任务详细说明1.框架构建public TimeFrame() {add(new ...

    Java课程设计--定时器

    1.团队课程设计博客链接

    2.个人负责模块或任务说明

    框架构建

    时间设置面板,倒计时面板

    按钮设置

    3.自己的代码提交记录截图

    f3c48ab542cb033c44178f7ed63c08a9.png

    4.自己负责模块或任务详细说明

    1.框架构建

    public TimeFrame() {

    add(new TimePanel());

    this.setTitle("定时器");

    this.setSize(1200, 800);

    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    this.setLocationRelativeTo(null);

    this.setResizable(false);

    this.setVisible(true);

    }

    2.创建类

    class TimePanel extends JPanel implements ActionListener, KeyListener, Runnable {

    /**

    *

    */

    private static final long serialVersionUID = 1L;

    private int setTime;

    private int showTime;

    // 定义是否计时变量

    private boolean flag = false;

    private JPanel jpShowTime, jpSetting, jpTimeInfo;

    private JLabel labTime, labSetTime, labPassTime, labRemainTime;

    private JButton btnBegin;

    private JButton btnPause;

    private JButton btnContinue;

    private JButton btnHidden;

    private JButton btnShow;

    private JButton btnExit;

    // 倒计时设置

    private JPanel jpTimeSetting;

    private JTextField tfdHours;

    private JTextField tfdMinutes;

    private JTextField tfdSeconds;

    public TimePanel() {

    this.setSize(1200, 800);

    this.setLayout(null);

    createSetting();

    createShowTime();

    jpSetting.addKeyListener(this);

    jpSetting.setFocusable(true);

    }

    3.创建倒计时面板、设置时间面板、倒计时属性面板、时间信息面板

    void createShowTime() {

    jpShowTime = new JPanel();

    jpShowTime.setSize(1200, 400);

    jpShowTime.setBackground(Color.BLACK);

    labTime = new JLabel("00 : 00 : 00", JLabel.CENTER);

    labTime.setFont(new Font("微软雅黑", 1, 200));

    labTime.setForeground(Color.RED);

    jpShowTime.add(labTime);

    add(jpShowTime).setBounds(0, 400, 1200, 400);

    }

    void createSetting() {

    jpSetting = new JPanel();

    jpSetting.setSize(1200, 400);

    jpSetting.setLayout(null);

    createTimeSetting();

    createTimeInfo();

    // 开始按钮

    btnBegin = new JButton("开始[F8]");

    jpSetting.add(btnBegin).setBounds(700, 50, 80, 50);

    // 暂停按钮

    btnPause = new JButton("暂停[F9]");

    jpSetting.add(btnPause).setBounds(800, 50, 80, 50);

    // 继续按钮

    btnContinue = new JButton("继续[F10]");

    jpSetting.add(btnContinue).setBounds(900, 50, 80, 50);

    // 隐藏按钮

    btnHidden = new JButton("隐藏[F11]");

    jpSetting.add(btnHidden).setBounds(700, 120, 80, 50);

    // 显示按钮

    btnShow = new JButton("显示[F12]");

    jpSetting.add(btnShow).setBounds(800, 120, 80, 50);

    // 帮助按钮

    btnExit = new JButton("退出");

    jpSetting.add(btnExit).setBounds(900, 120, 80, 50);

    btnBegin.addActionListener(this);

    btnPause.addActionListener(this);

    btnContinue.addActionListener(this);

    btnHidden.addActionListener(this);

    btnShow.addActionListener(this);

    btnExit.addActionListener(this);

    add(jpSetting).setBounds(0, 0, 1200, 400);

    }

    void createTimeSetting() {

    jpTimeSetting = new JPanel();

    jpTimeSetting.setSize(300, 200);

    jpTimeSetting.setLayout(new FlowLayout());

    jpTimeSetting.add(tfdHours = new JTextField(3));

    jpTimeSetting.add(new JLabel("时"));

    jpTimeSetting.add(tfdMinutes = new JTextField(3));

    jpTimeSetting.add(new JLabel("分"));

    jpTimeSetting.add(tfdSeconds = new JTextField(3));

    jpTimeSetting.add(new JLabel("秒"));

    tfdHours.addKeyListener(this);

    tfdMinutes.addKeyListener(this);

    tfdSeconds.addKeyListener(this);

    jpSetting.add(jpTimeSetting).setBounds(100, 100, 300, 200);

    }

    void createTimeInfo() {

    jpTimeInfo = new JPanel();

    jpTimeInfo.setLayout(null);

    jpTimeInfo.setBackground(new Color(154, 217, 250));

    jpTimeInfo.setSize(1200, 100);

    // 总秒数

    labSetTime = new JLabel("计时总秒数 : ", JLabel.CENTER);

    labSetTime.setFont(new Font("微软雅黑", 0, 20));

    jpTimeInfo.add(labSetTime).setBounds(0, 10, 400, 80);

    // 已过秒数

    labPassTime = new JLabel("已过秒数 : ", JLabel.CENTER);

    labPassTime.setFont(new Font("微软雅黑", 0, 20));

    jpTimeInfo.add(labPassTime).setBounds(400, 10, 400, 80);

    // 剩余秒数

    labRemainTime = new JLabel("剩余秒数 : ", JLabel.CENTER);

    labRemainTime.setFont(new Font("微软雅黑", 0, 20));

    jpTimeInfo.add(labRemainTime).setBounds(800, 10, 400, 80);

    jpSetting.add(jpTimeInfo).setBounds(0, 300, 1200, 100);

    }

    5.课程设计感想

    虽然时间紧迫,但是做了组长还是要负责好课设,于是和组员一直讨论要怎么搞才好,沟通交流是做课设最主要的,分配好各自的工作然后讨论交流才让我们的课设最终完成。最后的最后,不懂的~要百度~

    展开全文
  • Java 定时器

    2019-08-03 17:35:36
    推荐阅读: springboot 定时任务 ...ScheduledExecutorService:也是 jdk 自带的一个基于线程池设计的定时任务类 其每个调度任务都会分配到线程池中的一个线程执行,所以其任务并发执行,互不影响 Spring...

    推荐阅读: springboot 定时任务

    一、简介

    定时任务的几种实现方式:

    • Timer:jdk 自带的定时调度类,可以简单的实现按某一频度进行任务执行

      功能比较单一,无法实现复杂的调度任务

    • ScheduledExecutorService:也是 jdk 自带的一个基于线程池设计的定时任务类

      其每个调度任务都会分配到线程池中的一个线程执行,所以其任务并发执行,互不影响

    • Spring Task:Spring 提供的任务调度工具,支持注解和配置文件形式,支持 Cron 表达式,使用简单但功能强大
    • Quartz:一款功能强大的任务调度器,可以实现较为复杂的调度功能,如每月一号执行、每天凌晨执行、每周五执行等等,还支持分布式调度,配置比较复杂

    二、Timer

    使用示例:

    public void doTimer() {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                log.info("Timer定时任务启动:" + new Date()); 
            }
        }, 1000,1000);//延迟1秒启动,每1秒执行一次
    }
    

    相关 API 说明

    • 在特定时间执行任务,只执行一次
      public void schedule(TimerTask task,Date time)
      
    • 在特定时间之后执行任务,只执行一次
      public void schedule(TimerTask task,long delay)
      
    • 指定第一次执行的时间,然后按照间隔时间,重复执行
      public void schedule(TimerTask task,Date firstTime,long period)
      
    • 在特定延迟之后第一次执行,然后按照间隔时间,重复执行
      public void schedule(TimerTask task,long delay,long period)
      
    • 第一次执行之后,特定频率重复执行
      public void scheduleAtFixedRate(TimerTask task,Date firstTime,long period)
      
    • 在delay毫秒之后第一次执行,后按照特定频率执行
      public void scheduleAtFixedRate(TimerTask task,long delay,long period)
      

    相关参数:

    • delay: 延迟执行的毫秒数,即在delay毫秒之后第一次执行
    • period:重复执行的时间间隔

    取消任务使用:timer.cancel() 方法即可注销任务

    • Timer 不支持多线程,串行执行
      而且也不捕获异常,假设某个任务异常了,整个Timer就无法运行

    三、ScheduledExecutorService

    ScheduledExecutorService 可视为 Timer 的替代类

    public void ScheduledExecutorService() {
        ScheduledExecutorService service = Executors.newScheduledThreadPool(10);
        service.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                log.info("ScheduledExecutorService定时任务执行:" + new Date());                
            }
        }, 1, 1, TimeUnit.SECONDS);//首次延迟1秒,之后每1秒执行一次
        log.info("ScheduledExecutorService定时任务启动:" + new Date());         
    }
    

    常见 API:

    • ScheduleAtFixedRate
      public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit);
      

      参数说明:

      • command:执行线程
      • initialDelay:初始化延时
      • period:两次开始执行最小间隔时间
      • unit:计时单位
    • ScheduleWithFixedDelay
      public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit);
      

      参数说明:

      • command:执行线程
      • initialDelay:初始化延时
      • delay:前一次执行结束到下一次执行开始的间隔时间(间隔执行延迟时间)
      • unit:计时单位

    四、Spring Task

    1. 注解

    • 在启动类中加入 @EnableScheduling 使注解 @Scheduled 生效
      @SpringBootApplication
      @EnableScheduling
      public class Application {
      	public static void main(String[] args) {
          	SpringApplication.run(Application.class, args);
          }
      }
      
    • 编写一个调度类,系统启动后自动扫描,自动执行
      @Component
      public class ScheduledTask {
      	//自动扫描,启动时间点之后 5 秒执行一次
      	@Scheduled(fixedRate=5000)
      	public void getCurrentDate() {
          	log.info("Scheduled定时任务执行:" + new Date());
      	}
      }
      

    注解 @Scheduled 参数详解:

    • fixedRate:上一次开始执行时间点之后多长时间再执行
    • fixedRateString: 同上,只是使用字符串的形式,唯一不同的是支持占位符
    • fixedDelay:定义按一定频率执行的定时任务,改属性可以配合 initialDelay, 定义该任务延迟执行时间
    • fixedDelayString: 同上,只是使用字符串的形式,唯一不同的是支持占位符
    • initialDelay: 第一次延迟多长时间后再执行
    • initialDelayString: 同上,只是使用字符串的形式,唯一不同的是支持占位符
    • cron:通过表达式来配置任务执行时间

    2. Cron表达式

    • 该参数接收一个 cron 表达式,cron 表达式是一个字符串,字符串以5或6个空格隔开,分开共6或7个域,每一个域代表一个含义
    • Cron 语法
      //注:[年]不是必须的域,可以省略[年],则一共6个域
      [] [] [小时] [] [] [] []
      
    序号 说明 必填 允许填写的值 允许的通配符
    1 0-59 , - * /
    2 0-59 , - * /
    3 0-23 , - * /
    4 1-31 , - * ? / L W
    5 1-12 or JAN-DEC , - * /
    6 1-7 or SUN-SAT , - * ? / L #
    7 1970-2099 , - * /

    通配符说明:

    • * 表示所有值
    • ? 表示不指定值

      例如: 要在每月的10号触发一个操作,但不关心是周几,则设置为 0 0 0 10 * ?

    • - 表示区间

      例如: 在小时上设置 “10-12”,表示 10,11,12点都会触发

    • , 表示指定多个值

      例如: 在周字段上设置 “MON,WED,FRI” 表示周一,周三和周五触发

    • / 用于递增触发

      例如: 在秒上面设置”5/15” 表示从5秒开始,每增15秒触发

    • L 表示最后的意思
      • 在日字段设置上,表示当月的最后一天
      • 在周字段上表示星期六,相当于”7”或”SAT”

      例如: 在周字段上设置”6L”这样的格式,则表示“本月最后一个星期五”

    • W 表示离指定日期的最近那个工作日(周一至周五)

      例如: 在日字段上置”15W”,表示离每月15号最近的那个工作日触发

      • 如果15号正好是周六,则找最近的周五(14号)触发,
      • 如果15号是周未,则找最近的下周一(16号)触发
      • 如果15号正好在工作日(周一至周五),则就在该天触发
    • # 序号(表示每月的第几个周几)

      例如: 在周字段上设置”6#3”表示在每月的第三个周六

    3. 自定义线程池

    • 编写配置类,同时启用 @Async 注解
      @Configuration
      @EnableAsync
      public class Config {
      	//配置线程池
      	@Bean(name = "scheduledPoolTaskExecutor")
      	public ThreadPoolTaskExecutor getAsyncThreadPoolTaskExecutor() {
          	ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
          	taskExecutor.setCorePoolSize(20);
          	taskExecutor.setMaxPoolSize(200);
          	taskExecutor.setQueueCapacity(25);
          	taskExecutor.setKeepAliveSeconds(200);
          	taskExecutor.setThreadNamePrefix("oKong-Scheduled-");
          	// 线程池对拒绝任务(无线程可用)的处理策略,目前只支持AbortPolicy、CallerRunsPolicy;默认为后者
          	taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
          	//调度器shutdown被调用时等待当前被调度的任务完成
          	taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
          	//等待时长
          	taskExecutor.setAwaitTerminationSeconds(60);
          	taskExecutor.initialize();
          	return taskExecutor;
      	}
      }
      
    • 调度类上加入 @Async
      @Component
      public class ScheduledTask {
      	//自动扫描,启动时间点之后5秒执行一次
      	@Async("scheduledPoolTaskExecutor")
      	@Scheduled(fixedRate=5000)
      	public void getCurrentDate() {
          	log.info("Scheduled定时任务执行:" + new Date());
      	}
      }
      

    4. 动态添加定时任务

    使用注解无法实现动态的修改或者添加新的定时任务
    可直接使用 ThreadPoolTaskScheduler 或 SchedulingConfigurer 接口进行自定义定时任务创建

    ThreadPoolTaskScheduler 方式:

    ThreadPoolTaskScheduler 是 SpringTask 的核心实现类,该类提供了大量的重载方法进行任务调度

    • 创建一个 ThreadPoolTaskScheduler
      @Bean("taskExecutor")
      public TaskScheduler taskExecutor() {
      	ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
      	executor.setPoolSize(20);
      	executor.setThreadNamePrefix("oKong-taskExecutor-");
      	executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
      	//调度器shutdown被调用时等待当前被调度的任务完成
      	executor.setWaitForTasksToCompleteOnShutdown(true);
      	//等待时长
      	executor.setAwaitTerminationSeconds(60);
      	return executor;
      }
      
    • 编写一个控制类,动态设置定时任务
      @Autowired
      TaskScheduler taskScheduler;
      
      public void threadPoolTaskScheduler() {
      	taskScheduler.schedule(new Runnable() {
          	@Override
          	public void run() {
              	log.info("ThreadPoolTaskScheduler定时任务:" + new Date());
          	}
      	}, new CronTrigger("0/3 * * * * ?"));//每3秒执行一次
      }
      

    SchedulingConfigurer 方式:

    此类十个接口,直接实现其 configurerTasks 方法即可

    • 编写配置类
      @Configuration
      public class ScheduleConfig implements SchedulingConfigurer {
      	@Override
      	public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
          	taskRegistrar.setTaskScheduler(taskExecutor());
          	taskRegistrar.getScheduler().schedule(new Runnable() {
              	@Override
              	public void run() {
                  	log.info("SchedulingConfigurer定时任务:" + new Date());
              	}
          	}, new CronTrigger("0/3 * * * * ?"));//每3秒执行一次
      	}
      
      	@Bean("taskExecutor")
      	public TaskScheduler taskExecutor() {
          	ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
              executor.setPoolSize(20);
          	executor.setThreadNamePrefix("oKong-Executor-");
          	executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
          	//调度器shutdown被调用时等待当前被调度的任务完成
          	executor.setWaitForTasksToCompleteOnShutdown(true);
          	//等待时长
          	executor.setAwaitTerminationSeconds(60);
          	return executor;
      	}
      }
      

    五、Quartz

    maven 依赖:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-quartz</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    

    编写配置类:

    @Configuration
    public class QuartzConfig {   
        //通过工厂类,创建job实例
        public MethodInvokingJobDetailFactoryBean customJobDetailFactoryBean() {
            MethodInvokingJobDetailFactoryBean jobDetail = new MethodInvokingJobDetailFactoryBean();
            //设置执行任务的bean
            jobDetail.setTargetBeanName("quartzTask");
            //设置具体执行的方法
            jobDetail.setTargetMethod("quartzTask");
            //同步执行,上一任务未执行完,下一任务等待
            //true 任务并发执行
            //false 下一个任务必须等待上一任务完成
            jobDetail.setConcurrent(false);
            return jobDetail; 
        }
        
        //通过工厂类创建Trigger
        @Bean(name = "cronTriggerBean")
        public Trigger cronTriggerBean(MethodInvokingJobDetailFactoryBean jobDetailFactoryBean) throws ParseException {
            CronTriggerFactoryBean cronTriggerFactoryBean = new CronTriggerFactoryBean();
            cronTriggerFactoryBean.setJobDetail(jobDetailFactoryBean.getObject());
            cronTriggerFactoryBean.setCronExpression("0/3 * * * * ?");//每3秒执行一次
            cronTriggerFactoryBean.setName("customCronTrigger");
            cronTriggerFactoryBean.afterPropertiesSet();
            return cronTriggerFactoryBean.getObject();
        }
        
        //调度工厂类,自动注入Trigger
        @Bean
        public SchedulerFactoryBean schedulerFactoryBean(Trigger... triggers) {
            SchedulerFactoryBean bean = new SchedulerFactoryBean();
            
            //也可以直接注入 ApplicationContext,利于 getBeansOfType获取trigger
    //        Map<String,Trigger> triggerMap = appContext.getBeansOfType(Trigger.class);
    //        if(triggerMap != null) {
    //            List<Trigger> triggers = new ArrayList<>(triggerMap.size());
    //            //
    //            triggerMap.forEach((key,trigger)->{
    //                triggers.add(trigger);
    //            });
    //            bean.setTriggers(triggers.toArray(new Trigger[triggers.size()]));
    //        }
            //这里注意 对应的trigger 不能为null 不然会异常的
            bean.setTriggers(triggers);
            return bean;
        } 
        
        @Component("quartzTask")
        public class QuartzTask {
            public void quartzTask() {
                log.info("Quartz定时任务:" + new Date());
            }
        }
    }
    

    六、分布式定时任务

    分布式定时任务解决方案:

    • 剥离所有定时任务到一个工程:适用于定时任务相对较小,并发任务不多
    • 利用 Quartz 集群方案:Quartz 支持通过数据库实现集群

    集群架构图:
    在这里插入图片描述
    实现原理

    • 通过数据库实现任务的持久化,保存定时任务的相关配置信息,以保证下次系统启动时,定时任务能自动启动
    • 同时,通过数据库行锁(for update)机制,控制一个任务只能被一个实例运行,只有获取锁的实例才能运行任务,其他的只能等待,直到锁被释放

      弊端: 依赖数据库,同时要保证各服务器之间的时间同步,不然也会混乱

    基于 Redis 的集群方案:

    • 分布式锁:通过使用 RedisZooKeeper 实现分布式锁的机制,使得只有获取到锁的实例方能运行定时任务,避免任务重复执行

    • 统一调度中心:构建一个纯粹的定时服务,只有定时器相关配置,具体的任务执行操作都在各自业务方系统中,调度中心只负责接口的调用,具体实现还是在业务方

      需要业务方进行约定编程,或者对外提供一个 api 接口

      为实现定时任务的自动发现和注册功能,还要规范规则来实现自动注册功能,以Dubbo服务为例:

      • 可以定义一个定时任务接口类,调度中心只需获取所有实现此接口的服务
      • 同时通过服务的相关配置(调度时间、失败策略等)进行相关定时操作
      • 或者编写一个服务注册与发现的客户端,通过Spring获取到实现此接口的所有实现类,上送到调度中心

      统一调度中心,还可以对所有的定时任务的调度情况进行有效监控,日志记录等,也可以约定接口,让定时任务回传定时结果,做到全局把控

    展开全文
  • 设计定时器模块的设计方法非常多,但关键是定时器的效率问题。让我们先从最简单的开始吧。一、最简单的定时器一个最简单的定时器功能可以按如下思路实现:void WebSocketServer::doCheckHeartbeat(){ while (m_...

    定时器模块是后端服务常用的功能之一,用于需要周期性的执行某些任务的场景。设计定时器模块的设计方法非常多,但关键是定时器的效率问题。让我们先从最简单的开始吧。

    一、最简单的定时器

    一个最简单的定时器功能可以按如下思路实现:

    void WebSocketServer::doCheckHeartbeat(){    while (m_bRunning)    {        //休眠3秒        sleep(3000)                //检测所有会话的心跳        checkSessionHeartbeat();    }}

    上述代码在一个新的线程中每隔 3 秒对所有连接会话做心跳检测。这是一个非常简单的实现逻辑,读者可能会觉得有点不可思议,直接使用 sleep 函数休眠 3 秒作为时间间隔,这未免有点“简单粗暴”了吧?这段代码来源于我们之前一个商业项目,并且它至今工作的很好。所以凡事都没有那么绝对,在一些特殊场景下我们确实可以按这种思路来实现定时器,只不过 sleep 函数可能换成一些可以指定超时或等待时间的、让线程挂起或等待的函数(如 select、poll 等)。

    但是上述实现定时器的方法毕竟适用场景太少,也不能与 one thread one loop 结构相结合,one thread one loop 结构中的定时器才是我们本文的重点。你可以认为前面介绍的定时器实现只是个热身,现在让我们正式开始。

    二、定时器设计的基本思路

    根据实际的场景需求,我们的定时器对象一般需要一个唯一标识、过期时间、重复次数、定时器到期时触发的动作,因此一个定时器对象可以设计成如下结构:

    typedef std::function TimerCallback;//定时器对象class Timer{    Timer() ;    ~Timer();    void run()    {        callback();    }        //其他实现下文会逐步完善...private:    //定时器的id,唯一标识一个定时器    int64_t         m_id;    //定时器的到期时间    time_t          m_expiredTime;    //定时器重复触发的次数    int32_t         m_repeatedTimes;    //定时器触发后的回调函数    TimerCallback   m_callback;};

    在 one loop one thread 结构中,我们提到使用用到定时器的程序结构:

    while (!m_bQuitFlag){	check_and_handle_timers();		epoll_or_select_func();	handle_io_events();	handle_other_things();}

    我们在函数 check_and_handle_timers() 中对各个定时器对象进行处理(检测是否到期,如果到期调用相应的定时器函数),我们先从最简单的情形开始讨论,将定时器对象放在一个 std::list 对象中:

    //m_listTimers可以是EventLoop的成员变量std::list m_listTimers;void EventLoop::check_and_handle_timers(){    for (auto& timer : m_listTimers)    {        //判断定时器是否到期        if (timer->isExpired())        {            timer->run();        }    }}

    为了方便管理所有定时器对象,我们可以专门新建一个 TimerManager 类去对定时器对象进行管理,该对象提供了增加、移除和判断定时器是否到期等接口:

    class TimerManager{public:    TimerManager() = default;    ~TimerManager() = default;    /** 添加定时器     * @param repeatedCount 重复次数     * @param 触发间隔     * @     * @return 返回创建成功的定时器id     */    int64_t addTimer(int32_t repeatedCount, int64_t interval, const TimerCallback& timerCallback);    /** 移除指定id的定时器     * @param timerId 待移除的定时器id     * @return 成功移除定时器返回true,反之返回false     */    bool removeTimer(int64_t timerId);    /** 检测定时器是否到期,如果到期则触发定时器函数     */    void checkAndHandleTimers();private:    std::list m_listTimers;};

    这样 check_and_handle_timers() 调用实现变成如下形式:

    void EventLoop::check_and_handle_timers(){    //m_timerManager可以是EventLoop的成员变量    m_timerManager.checkAndHandleTimers();}

    addTimerremoveTimercheckAndHandleTimers 的实现如下:

    int64_t TimerManager::addTimer(int32_t repeatedCount, int64_t interval, const TimerCallback& timerCallback){    Timer* pTimer = new Timer(repeatedCount, interval, timerCallback);    m_listTimers.push_back(pTimer);    return pTimer->getId();}bool TimerManager::removeTimer(int64_t timerId){    for (auto iter = m_listTimers.begin(); iter != m_listTimers.end(); ++iter)    {        if ((*iter)->getId() == timerId)        {            m_listTimers.erase(iter);            return true;        }    }    return false;}void TimerManager::checkAndHandleTimers(){    Timer* deletedTimer;    for (auto iter = m_listTimers.begin(); iter != m_listTimers.end(); )    {        if ((*iter)->isExpired())        {            (*iter)->run();                        if ((*iter)->getRepeatedTimes() == 0)            {                //定时器不需要重复,从集合中移除该对象                deletedTimer = *iter;                iter = m_listTimers.erase(iter);                delete deletedTimer;                continue;            }            else             {                ++iter;            }        }    }}

    addTimer 函数传递必要的参数后创建一个 Timer 对象,并返回唯一标识该定时器对象的 id,后续步骤就可以通过定时器 id 来操作这个定时器对象。

    我这里的定时器 id 使用了一个单调递增的 int64_t 的整数,你也可以使用其他类型,如 uid,只要能唯一区分每个定时器对象即可。当然,在我这里的设计逻辑中,可能多个线程多个 EventLoop,每一个 EventLoop 含有一个 m_timerManager 对象,但我希望所有的定时器的 id 能够全局唯一,所以我这里每次生成定时器 id 时使用了一个整型原子变量的 id 基数,我将它设置为 Timer 对象的静态成员变量,每次需要生成新的定时器 id 时将其递增 1 即可,这里我利用 C++ 11 的 std::mutex 对  s_initialId 进行保护。

    //Timer.hclass Timer{public:	Timer::Timer(int32_t repeatedTimes, int64_t interval, const TimerCallback& timerCallback);	~Timer() {}		bool isExpired();    void run()    {        callback();    }    	//其他无关代码省略...	public:	//生成一个唯一的id	static int64_t generateId();	private:    //定时器id基准值,初始值为 0    static int64_t     s_initialId{0};    //保护s_initialId的互斥体对象    static std::mutex  s_mutex{};	   };
    //Timer.cppint64_t Timer::generateId(){	int64_t tmpId;	s_mutex.lock();	++s_initialId;	tmpId = s_initialId;	s_mutex.unlock();		return tmpId;	}Timer::Timer(int32_t repeatedTimes, int64_t interval, const TimerCallback& timerCallback){    m_repeatedTimes = repeatedTimes;    m_interval = interval;    //当前时间加上触发间隔得到下一次的过期时间    m_expiredTime = (int64_t)time(nullptr) + interval;    m_callback = timerCallback;    m_id = Timer::generateId();}

    定时器的下一次过期时间 m_expiredTime 是添加定时器的时间点加上触发间隔 interval,即上述代码第 9 行,也就是说我这里使用绝对时间点作为定时器的过期时间,读者在自己的实现时也可以使用相对时间间隔。

    在我的实现中,定时器还有个表示触发次数的变量:m_repeatedCount,m_repeatedCount 为 -1 时表示不限制触发次数(即一直触发次数),m_repeatedCount 大于 0 时,每触发一次,m_repeatedCount 递减 1,一直到 m_repeatedCount 等于 0 从定时器集合中移除。

    void TimerManager::checkAndHandleTimers(){    Timer* deletedTimer;    for (auto iter = m_listTimers.begin(); iter != m_listTimers.end(); )    {        if ((*iter)->isExpired())        {            //执行定时器事件            (*iter)->run();                        if ((*iter)->getRepeatedTimes() == 0)            {                //定时器不需要重复触发从集合中移除该对象                deletedTimer = *iter;                iter = m_listTimers.erase(iter);                delete deletedTimer;                continue;            }            else             {                ++iter;            }        }    }}

    上述代码中我们先遍历定时器对象集合,然后调用 Timer::isExpired() 函数判断当前定时器对象是否到期,该函数的实现如下:

    bool Timer::isExpired(){    int64_t now = time(nullptr);    return now >= m_expiredTime;}

    实现很简单,即用定时器的到期时间与当前系统时间做比较。

    如果一个定时器已经到期了,则执行定时器 Timer::run(),该函数不仅调用定时器回调函数,还更新定时器对象的状态信息(如触发的次数和下一次触发的时间点):

    void Timer::run(){    m_callback();    if (m_repeatedTimes >= 1)    {        --m_repeatedTimes;    }				//计算下一次的触发时间    m_expiredTime += m_interval;}

    除了定时器触发次数变为 0 时会从定时器列表中移除,也可以调用 removeTimer() 函数主动从定时器列表中移除一个定时器对象:

    bool TimerManager::removeTimer(int64_t timerId){    for (auto iter = m_listTimers.begin(); iter != m_listTimers.end(); ++iter)    {        if ((*iter)->getId() == timerId)        {            m_listTimers.erase(iter);            return true;        }    }    return false;}

    removeTimer() 函数成功通过一个定时器 id 成功移除一个定时器对象时会返回 true,反之返回 false。

    我们再贴下完整的代码:

    Timer.h

    #ifndef __TIMER_H__#define __TIMER_H__#include typedef std::function TimerCallback;class Timer{public:    /**     * @param repeatedTimes 定时器重复次数,设置为-1表示一直重复下去     * @param interval      下一次触发的时间间隔     * @param timerCallback 定时器触发后的回调函数     */    Timer(int32_t repeatedTimes, int64_t interval, const TimerCallback& timerCallback);    ~Timer();    int64_t getId()    {        return m_id;    }    bool isExpired();    int32_t getRepeatedTimes()    {        return m_repeatedTimes;    }    void run();    //其他实现暂且省略    public:	//生成一个唯一的id	static int64_t generateId();private:    //定时器的id,唯一标识一个定时器    int64_t                     m_id;    //定时器的到期时间    time_t                      m_expiredTime;    //定时器重复触发的次数    int32_t                     m_repeatedTimes;    //定时器触发后的回调函数    TimerCallback               m_callback;    //触发时间间隔                    int64_t                     m_interval;    //定时器id基准值,初始值为 0    static int64_t     			s_initialId{0};    //保护s_initialId的互斥体对象    static std::mutex  			s_mutex{};	};#endif //!__TIMER_H__

    Timer.cpp

    #include "Timer.h"#include int64_t Timer::generateId(){	int64_t tmpId;	s_mutex.lock();	++s_initialId;	tmpId = s_initialId;	s_mutex.unlock();		return tmpId;	}Timer::Timer(int32_t repeatedTimes, int64_t interval, const TimerCallback& timerCallback){    m_repeatedTimes = repeatedTimes;    m_interval = interval;    //当前时间加上触发间隔得到下一次的过期时间    m_expiredTime = (int64_t)time(nullptr) + interval;    m_callback = timerCallback;    //生成一个唯一的id    m_id = Timer::generateId();}bool Timer::isExpired() const{    int64_t now = time(nullptr);    return now >= m_expiredTime;}void Timer::run(){    m_callback();    if (m_repeatedTimes >= 1)    {        --m_repeatedTimes;    }    m_expiredTime += m_interval;}

    TimerManager.h

    #ifndef __TIMER_MANAGER_H__#define __TIMER_MANAGER_H__#include #include #include "Timer.h"void defaultTimerCallback(){}class TimerManager{public:    TimerManager() = default;    ~TimerManager() = default;    /** 添加定时器     * @param repeatedCount 重复次数     * @param interval      触发间隔     * @param timerCallback 定时器回调函数     * @return              返回创建成功的定时器id     */    int64_t addTimer(int32_t repeatedCount, int64_t interval, const TimerCallback& timerCallback);    /** 移除指定id的定时器     * @param timerId 待移除的定时器id     * @return 成功移除定时器返回true,反之返回false     */    bool removeTimer(int64_t timerId);    /** 检测定时器是否到期,如果到期则触发定时器函数     */    void checkAndHandleTimers();private:    std::list m_listTimers;};#endif //!__TIMER_MANAGER_H__

    TimerManager.cpp

    #include "TimerManager.h"    int64_t TimerManager::addTimer(int32_t repeatedCount, int64_t interval, const TimerCallback& timerCallback){    Timer* pTimer = new Timer(repeatedCount, interval, timerCallback);    m_listTimers.push_back(pTimer);    return pTimer->getId();}bool TimerManager::removeTimer(int64_t timerId){    for (auto iter = m_listTimers.begin(); iter != m_listTimers.end(); ++iter)    {        if ((*iter)->getId() == timerId)        {            m_listTimers.erase(iter);            return true;        }    }    return false;}void TimerManager::checkAndHandleTimers(){    Timer* deletedTimer;    for (auto iter = m_listTimers.begin(); iter != m_listTimers.end(); )    {        if ((*iter)->isExpired())        {            (*iter)->run();                        if ((*iter)->getRepeatedTimes() == 0)            {                //定时器不需要触发从集合中移除该对象                deletedTimer = *iter;                iter = m_listTimers.erase(iter);                delete deletedTimer;                continue;            }            else             {                ++iter;            }        }    }}

    以上就是定时器的设计的基本思路,你一定要明白在在这个流程中一个定时器对象具有哪些属性,以及定时器对象该如何管理。当然,这里自顶向下一共三层结构,分别是 EventLoop、TimerManager、Timer,其中 TimerManager 对象不是必需的,在一些设计中直接用 EventLoop 封装相应方法对 Timer 对象进行管理。

    理解了 one thread one loop 中定时器的设计之后,我们来看下上述定时器实现中的性能问题。

    三、定时器效率优化

    上述定时器实现中存在严重的性能问题,即每次我们检测定时器对象是否触发都要遍历定时器集合,移除定时器对象时也需要遍历定时器集合,其实我们可以将定时器按过期时间从小到大排序,这样我们检测定时器对象时,只要从最小的过期时间开始检测,一旦找到过期时间大于当前时间的定时器对象,后面的定时器对象就不需要再判断了。

    1. 定时器对象集合的数据结构优化一

    我们可以在每次将定时器对象添加到集合时自动进行排序,如果我们仍然使用 std::list 作为定时器集合,我们可以给 std::list 自定义一个排序函数(从小到大排序)。实现如下:

    //Timer.hclass Timer{public:		//无关代码省略...    int64_t getExpiredTime() const    {        return m_expiredTime;    }};
    //TimerManager.hstruct TimerCompare  {      bool operator() (const Timer* lhs, const Timer* rhs)      {          return lhs->getExpiredTime() <  rhs->getExpiredTime();    }}

    每次添加定时器时调用下自定义排序函数对象 TimerCompare (代码第 8 行):

    int64_t TimerManager::addTimer(int32_t repeatedCount, int64_t interval, const TimerCallback& timerCallback){    Timer* pTimer = new Timer(repeatedCount, interval, timerCallback);    m_listTimers.push_back(pTimer);    //对定时器对象按过期时间从小到大排序    m_listTimers.sort(TimerCompare());    return pTimer->getId();}

    现在我们检测排序定时器是否触发就可以按过期时间从最小的一直找到大于当前系统时间的定时器对象就可以结束了:

    void TimerManager::checkAndHandleTimers(){    //遍历过程中是否调整了部分定时器的过期时间    bool adjusted = false;    Timer* deletedTimer;        for (auto iter = m_listTimers.begin(); iter != m_listTimers.end(); )    {        if ((*iter)->isExpired())        {            (*iter)->run();                        if ((*iter)->getRepeatedTimes() == 0)            {                //定时器不需要再触发时从集合中移除该对象                deletedTimer = *iter;                iter = m_listTimers.erase(iter);                delete deletedTimer;                continue;            }            else             {                ++iter;                //标记下集合中有定时器调整了过期时间                adjusted = true;            }        }        else        {            //找到大于当前系统时间的定时器对象就不需要继续往下检查了,退出循环            break;        }// end if          }// end for-loop    //由于调整了部分定时器的过期时间,需要重新排序一下    if (adjusted)    {        m_listTimers.sort(TimerCompare());    }}

    上述代码中有个细节需要注意:假设现在系统时刻是 now,定时器集合中定时器过期时间从小到大依次为 t1、t2、t3、t4、t5 ... tn,假设 now < t4 且 now > t5,即 t1、t2、t3、t4 对应的定时器会触发,触发后,会从 t1、t2、t3、t4 减去对应的时间间隔,减去之后,新的 t1’、t2‘、t3‘、t4’ 就不一定小于 t5 ~ tn 了,因此需要再次对定时器集合进行排序。但是,存在一种情形:如果 t1~t5 正好触发后其对应的触发次数变为 0,因此需要从定时器列表中移除它们,这种情形下又不需要对定时器列表进行排序。因此上述代码使用了一个 adjusted 变量以记录是否有过期时间被更新且未从列表中移除的定时器对象,如果有则之后再次对定时器集合进行排序。

    上述设计虽然解决了定时器遍历效率低下的问题,但是没法解决移除一个定时器仍然需要遍历的问题, 使用链表结构的 std::list 插入非常方便但定位某个具体的元素效率就比较低了。我们将 std::list 换成 std::map 再试试,当然我们仍然需要对 std::map 中的定时器对象按过期时间进行自定义排序。

    2. 定时器对象集合的数据结构优化二

    Timer.h 和 Timer.cpp 文件保持不变,修改后的 TimerManager.h 与 TimerManager.cpp 文件如下:

    TimerManager.h

    #ifndef __TIMER_MANAGER_H__#define __TIMER_MANAGER_H__#include #include #include "Timer.h"struct TimerCompare  {      bool operator () (const Timer* lhs, const Timer* rhs)      {          return lhs->getExpiredTime() <  rhs->getExpiredTime();    }}; void defaultTimerCallback(){}class TimerManager{public:    TimerManager() = default;    ~TimerManager() = default;    /** 添加定时器     * @param repeatedCount 重复次数     * @param interval      触发间隔     * @param timerCallback 定时器回调函数     * @return              返回创建成功的定时器id     */    int64_t addTimer(int32_t repeatedCount, int64_t interval, const TimerCallback& timerCallback);    /** 移除指定id的定时器     * @param timerId 待移除的定时器id     * @return 成功移除定时器返回true,反之返回false     */    bool removeTimer(int64_t timerId);    /** 检测定时器是否到期,如果到期则触发定时器函数     */    void checkAndHandleTimers();private:    //key是定时器id,value是定时器对象,注意模板的第三个参数是自定义排序对象TimerCompare    std::map   m_mapTimers;};#endif //!__TIMER_MANAGER_H__

    TimerManager.cpp

    #include "TimerManager.h"    int64_t TimerManager::addTimer(int32_t repeatedCount, int64_t interval, const TimerCallback& timerCallback){    Timer* pTimer = new Timer(repeatedCount, interval, timerCallback);    int64_t timerId = pTimer->getId();    //插入时会自动按TimerCompare对象进行排序    m_mapTimers[timerId] = pTimer;    return timerId;}bool TimerManager::removeTimer(int64_t timerId){    auto iter = m_mapTimers.find(timerId);    if (iter != m_mapTimers.end())    {        m_mapTimers.erase(iter);        return true;    }    return false;}void TimerManager::checkAndHandleTimers(){    //遍历过程中是否调整了部分定时器的过期时间    bool adjusted = false;    for (auto iter = m_mapTimers.begin(); iter != m_mapTimers.end(); )    {        if (iter->second->isExpired())        {            iter->second->run();                        if (iter->second->getRepeatedTimes() == 0)            {                //定时器不需要触发从集合中移除该对象                iter = m_mapTimers.erase(iter);                delete (iter->second);                continue;            }            else             {                ++iter;                //标记下集合中有定时器调整了过期时间                adjusted = true;            }        }        else        {            //找到大于当前系统时间的定时器对象就不需要继续往下检查了,退出循环            break;        }            }    //由于调整了部分定时器的过期时间,需要重新排序一下    if (adjusted)    {        std::map localMapTimers;            for (const auto& iter : m_mapTimers)        {            localMapTimers[iter.first] = iter.second;        }        m_mapTimers.clear();        m_mapTimers.swap(localMapTimers);    }}

    上述实现中,无论使用 std::list 还是使用 std::map 后,当某个定时器对象需要调整过期时间后仍然要对整体进行排序,这样效率非常低。

    3. 定时器对象集合的数据结构优化三

    实际上,为了追求定时器的效率,我们一般有两种常用的方法,时间轮时间堆

    时间轮

    我们先来介绍时间轮的如何实现。

    时间轮的基本思想是将从现在时刻 t 加上一个时间间隔 interval,以 interval 为步长,将各个定时器对象的过期时间按步长分布在不同的时间槽(time slot)中,当一个时间槽中出现多个定时器对象时,这些定时器对象按加入槽中的顺序串成链表,时间轮的示意图如下:

    8427863be0c7f502420270e8098f7471.png

    因为每个时间槽的时间间隔是一定的,因此对时间轮中的定时器对象的检测会有两种方法:

    第一种方法在每次检测时判断当前系统时间处于哪个时间槽中,比该槽序号小的槽中的所有定时器都已到期,执行对应的定时器函数之后,移除不需要重新触发的定时器,或重新计算需要下一次触发的定时器对象的时间并重新计算将其移到新的时间槽中。这个适用于我们上文说的 one loop one thread 结构。

    第二种方法,即每次检测时假设当前的时间与之前相比跳动了一个时间轮的间隔,这种方法适用场景比较小,也不适用于我们这里介绍的 one loop one thread 结构。

    时间轮的本质实际上将一个链表按时间分组,虽然提高了一些效率,但是效率上还是存在一个的问题,尤其是当某个时间槽对应的链表较长时。

    时间堆

    再来说时间堆,所谓时间堆其实就是利用数据结构中的小根堆(Min Heap)来组织定时器对象,根据到期时间的大小来组织。小根堆示意图如下:

    d106080bd0e11ac8f02db1e413154f4e.png

    如图所示,图中小根堆的各个节点代表一个定时器对象,它们按过期时间从小到大排列。使用小根堆在管理定时器对象和执行效率上都要优于前面方案中 std::list 和 std::map,这是目前一些主流网络库中涉及到定时器部分的实现,如 Libevent。我在实际项目中会使用 stl 提供的优先队列,即 std::priority_queue,作为定时器的实现,使用 std::priority_queue 的排序方式是从小到大,这是因为 std::priority 从小到大排序时其内部实现数据结构也是小根堆。

    四、对时间的缓存

    在使用定时器功能时,我们免不了要使用获取系统时间的函数,而在大多数操作系统上获取系统时间的函数属于系统调用,一次系统调用相对于 one thread one loop 结构中的其他逻辑来说可能耗时更多,因此为了提高效率,在一些对时间要求精度不是特别高的情况,我们可能会缓存一些时间,在较近的下次如果需要系统时间,可以使用上次缓存的时间,而不是再次调用获取系统时间的函数,目前不少网络库和商业服务在定时器逻辑这一块都使用这一策略。

    上述逻辑的伪码如下:

    while (!m_bQuitFlag){	//在这里第一次获取系统时间,并缓存之	get_system_time_and_cache();		//利用上一步获取的系统时间做一些耗时短的事情	do_something_fast_with_system_timer();		//这里可以不用再次获取系统时间,而是利用第一步缓存的时间作为当前系统时间	use_cached_time_to_check_and_handle_timers();		epoll_or_select_func();	handle_io_events();	handle_other_things();}

    五、小结

    定时器的基本实现原理和逻辑并不复杂,核心关键点是如何设计出高效的定时器对象集合数据结构,使每次的从定时器集合中增加、删除、修改和遍历定时器对象更高效。另外,为了进一步提高定时器逻辑的执行效率,在某些场景下可能会利用上次缓存的时间去代替再一次的获取系统时间的系统调用。

    定时器的设计还有其他一些需要考虑的问题,例如如果服务器机器时间被认为调提前或者延后了怎么解决,以及定时器事件的时间精度等问题。

    限于作者经验水平有限,欢迎各位读者在文章下面就内容留言~

    推荐阅读

    服务器开发进阶专栏

    如果对后端开发感兴趣,想加入 高性能服务器开发微信交流群 一起交流,可以先加我微信 easy_coder,备注"加微信群",我拉你入群,备注不对不加哦

    014e3dfa5e335cbffa28d2e75f172a8f.png

    原创不易,请帮忙点赞、在看和转发 46b531b90024044792a3e21c15333ab6.gif

    展开全文
  • java 中的定时器设计

    2015-11-30 11:03:58
    1.如果需求简单的情况下,比如单线程下可以直接使用while循环配合Thread.sleep(1000)来暂停一秒 这里还是要对sleep有个认识,sleep的时候并没有释放任何资源,占用着CPU睡觉,睡完继续当前线程的...import java....
  • 八、团队成员任务分配,团队成员课程设计博客链接(以表格形式呈现),标明组长。 姓名 学号 任务分配 博客地址 陈文俊[组长] 201521123047 框架构建、时间设置面板、倒计时面板、按钮设置 郑子熙 201521123045 main...
  • 设计一个定时器并实现以下三种基本行为,函数原型已给出,可使用任意编程语言设计数据结构及实现,并尽可能高效地支持大量定时器:// 添加定时器:经过特定时间间隔后执行目标操作// 输入 ...
  • 设计一个定时器并实现以下三种基本行为,函数原型已给出,可使用任意编程语言设计数据结构及实现,并尽可能高效地支持大量定时器:// 添加定时器:经过特定时间间隔后执行目标操作// 输入 ...
  • 我的第一个java定时器

    千次阅读 2015-08-27 10:03:40
    接下来就去开发我的第一个java定时器吧,Java计时器实用程序允许您执行线程或任务在一个预先确定的将来的时间,并根据一组这些任务可以重复频率。 设计和实现一个计时器,会用到model-control-view(MVC)设计模式。 ...
  • Java--定时器问题

    2017-04-09 09:34:00
    定时器问题 ... 请设计一个定时器并实现以下三种基本行为,函数原型已给出,可使用任意编程语言设计数据结构及实现,并尽可能高效地支持大量定时器: // 添加定时器:经过特定时间间隔后执行目标...
  • 前面介绍了线程的几种运行方式,不管哪种方式,一旦调用了线程实例的start方法,都会立即启动线程的事务处理。...有别于一般的线程,Java为定时功能设计了专门的定时任务TimerTask,以及定时器Timer。其中...
  • #课程设计——定时器(201521123045 郑子熙) 1.团队课程设计博客链接 http://www.cnblogs.com/chendajia/p/7065730.html 2.个人负责模块或任务说明 负责main函数的编写。 负责TIme函数的编写,用于计算...
  • 设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编写、代码设计经验的总结。 使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性以及代码的结构更加清晰. 设计模式分类...
  • java中使用定时器

    2014-10-08 23:05:12
    基于线程池设计的 ScheduledExecutor。其设计思想是,每一个被调度的任务都会由线程池中一个线程去执行,因此任务是并发执行的,相互之间不会受到干扰。需 要注意的是,只有当任务的执行时间到来时,...
  • Java课程设计---定时器

    2017-06-22 14:13:00
    1.团队课程设计博客链接 http://www.cnblogs.com/ltykm/p/7063663.html 2.个人负责模块或任务说明 本人任务:构思界面,设计界面的板块和板块中的内容,美化界面。 3.自己的代码提交记录截图 4.自己负责模块或任务...
  • 24.14_设计模式(单例模式的Java代码体现Runtime类) A:Runtime类概述 每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。可以通过 getRuntime 方法获取当前运行时。 应用程序不...
  • Java生鲜电商平台-定时器,定时任务quartz的设计与架构 说明:任何业务有时候需要系统在某个定点的时刻执行某些任务,比如:凌晨2点统计昨天的报表,早上6点抽取用户下单的佣金。 对于Java开源生鲜电商平台而言...
  • 定时器设计

    千次阅读 2011-07-17 01:29:50
    1、传统的定时器实现 package cn.itcast.thread; import java.util.Date; import java.util.Timer; import java.util.TimerTask; public class TraditionalTimer { private static int count = 0; public ...
  • 定时器有很多种(一文完全理解定时器实现技术),基于升序的定时器时间链表是一种最直接的实现方式:即按照定时器时间到的时间顺序依次存放在一个链表中进行管理。但是这种链表存在效率的不足,就是当插入定时器的时候...
  • java第二十四天之学到辽~ 线程池 定时器 设计模式 1.1 线程间的等待唤醒机制 Object 类中 void wait () 在其他线程调用此对象的 notify () 方法或 notifyAll () 方法前,导致当前线程等待。 void wait (long ...
  • 今天我们来制作一个Java定时器 主要功能:使得一些需要固定间隔时间的事件能够如期执行 我们先来初步设计一下 对于要实现的事件,我们给一个接口 并且想到 对于主的线程(提供时延)以及 Timerworker线程 两个线程 ...
  • 24-java学习-Lock锁、死锁现象、线程池、定时器设计模式 目录: Lock锁 死锁现象 线程池 定时器 设计模式 1.线程间的等待唤醒机制 Object 类中 void wait () 在其他线程调用此对象的 notify () 方法或 ...
  • Java技术与应用;主要内容;1. JBuilder2006介绍;JBuilder简介;主要特点;主要功能;2.案例的面向对象 程序设计;虚拟酒店点菜系统;功能 ;分析结果;设计结果;设计结果(2;程序设计;案例的打包与运行;关于案例的思考;3.线程...
  •  在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池 JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法  public static ExecutorService newCachedThreadPool(): 根据...
  • import java.awt.BorderLayout; import java.awt.Canvas; import java.awt.Color; import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event....
  • Java基础进阶_day16_(多线程,线程池,定时器,设计模式) Java基础进阶_day16_多线程线程池定时器设计模式 多线程 1 线程死锁 ...
  • 转:定时器设计

    2015-09-25 10:17:37
    1、传统的定时器实现   package cn.itcast.thread;    import java.util.Date;  import java.util.Timer;  import java.util.TimerTask;    public class TraditionalTimer {     ...
  • 主要介绍JKD自带的java.util.Timer定时器的实现原理. Timer使用本身很简单, 同样, 他的设计原理也很精妙. 如果你仅仅只是想知道如何在自己的程序中来使用java.util.Timer的一些方法,那么请移步: ...

空空如也

空空如也

1 2 3 4 5 ... 10
收藏数 199
精华内容 79
关键字:

java设计定时器

java 订阅