Android开发:轮询实现方式,Timer和ThreadPoolExecutor的对比

目录

 

一、Timer存在的问题

二、针对Timer的解决方法

三、通过ThreadPoolExecutor实现轮询


一、Timer存在的问题

1、首先 Timer 对调度的支持是基于绝对时间的,而不是相对时间,所以它对系统时间的改变非常敏感。

      系统时间修改到当前时间之后,不会影响Timer的执行;但是如果系统时间修改到当前时间之前,就会导致Timer挂起。

      说明:

       代码取自Timer >  mainLoop()   545-556行

currentTime = System.currentTimeMillis();
executionTime = task.nextExecutionTime;
if (taskFired = (executionTime<=currentTime)) {
    if (task.period == 0) { // Non-repeating, remove
        queue.removeMin();
        task.state = TimerTask.EXECUTED;
    } else { // Repeating task, reschedule
        queue.rescheduleMin(
          task.period<0 ? currentTime   - task.period
                        : executionTime + task.period);
    }
}

       注:period就是TimeTask初始化时设定的执行间隔

       从这段代码可以看出,TimerTask执行的条件是(executionTime<=currentTime)。其中executionTime取自TimerTask,而currentTime来自系统时间。currentTime因为修改系统时间而提前了,所以这个条件(executionTime<=currentTime)永远也不会达到,TimerTask将不会被执行。(避免方法:在修改系统时间后重新启动应用)

2、其次 Timer 线程是不会捕获异常的,如果 TimerTask 抛出的了未检查异常则会导致 Timer 线程终止,同时 Timer 也不会重新恢复线程的执行,他会错误的认为整个 Timer 线程都会取消。同时,已经被安排单尚未执行的 TimerTask 也不会再执行了,新的任务也不能被调度。故如果 TimerTask 抛出未检查的异常,Timer 将会产生无法预料的行为。”

3、Timer运行多个TimeTask任务时,只要其中一个挂掉了,其他的TimeTask也不会执行,而ScheduledExecutorService则不会。

二、针对Timer的解决方法

       对于 Timer 的缺陷,我们可以考虑 ScheduledThreadPoolExecutor 来替代。Timer 是基于绝对时间的,对系 
统时间比较敏感,而 ScheduledThreadPoolExecutor 则是基于相对时间;Timer 是内部是单一线程,而 Sch 
eduledThreadPoolExecutor 内部是个线程池,所以可以支持多个任务并发执行。
 

三、通过ThreadPoolExecutor实现轮询

1、这里封装了一个单例模式的工具类——ExecutorUtil,这是一个初始化线程池的工具类,代码如下:

/**
 * Created by  wsl
 * on 2019/5/10 10:34
 * 线程池工具类,用于解决阿里代码检测不允许使用Executors静态方法的问题
 */
public class ExecutorUtil {
    /**
     * 私有构造函数
     */
    private ExecutorUtil() {
    }

    /**
     * 内部类实现单例模式
     * 延迟加载,减少内存开销
     */
    private static class ExecutorHolder {
        private static ExecutorUtil executorUtil = new ExecutorUtil();
    }

    public static ExecutorUtil getInstance() {
        return ExecutorHolder.executorUtil;
    }

    /**
     * 线程池-缓存,可以无限制的创建线程,实现异步
     *
     * @return ThreadPoolExecutor
     */
    public ThreadPoolExecutor newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new CustomThreadFactory());
    }

    /**
     * 线程池-周期执行 使用scheduleAtFixedRate方法可以替代Timer
     *
     * @param corePoolSize 核心线程数
     * @return ScheduledThreadPoolExecutor
     */
    public ScheduledThreadPoolExecutor newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize, new CustomThreadFactory());
    }

    /**
     * 线程池-单一线程
     * 有且仅有一个工作线程执行任务
     * 所有任务按照指定顺序执行,即遵循队列的入队出队规则
     *
     * @return ThreadPoolExecutor
     */
    public ThreadPoolExecutor newSingleThreadExecutor() {
        return new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), new CustomThreadFactory());
    }

    /**
     * the factory to use when the executor creates a new thread
     */
    private class CustomThreadFactory implements ThreadFactory {
        private AtomicInteger count = new AtomicInteger(1);

        @Override
        public Thread newThread(Runnable runnable) {
            Thread thread = new Thread(runnable);
            String threadName = ExecutorUtil.class.getSimpleName() + count.addAndGet(1);
            thread.setName(threadName);
            return thread;
        }
    }
}

实现轮询,我们需要使用ExecutorUtil的newScheduledThreadPool()方法,在使用处对此方法进行初始化,然后调用下面方法进行轮询:

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

command:需要执行的任务,是一个Runnable

initialDelay:延迟时间

period:轮询周期

unit:单位,包括:MICROSECONDS:微妙、MILLISECONDS:毫秒、SECONDS:秒等,更多时间单位请参考TimeUnit 类

2、使用,在Activity中使用的例子如下:

private ScheduledThreadPoolExecutor buzzerTimer;
buzzerTimer = ExecutorUtil.getInstance().newScheduledThreadPool(1);
buzzerTimer.scheduleAtFixedRate(new Runnable() {
    @Override
    public void run() {
            //执行事件
            playMusicFromLocal(); 
    }
}, 0, 1000, TimeUnit.MILLISECONDS);
  @Override
    protected void onDestroy() {
        super.onDestroy();
        if (buzzerTimer != null && !buzzerTimer.isShutdown()) {
            buzzerTimer.shutdown();
        }
    }

 

以上是实现轮询的实现方法,延迟时间为0s、周期为1000ms。

如果playMusicFromLocal()要求不能延迟、堵塞(并发),就使用线程池的缓存机制,即上面线程池工具类中的newCachedThreadPool()方法实现异步;如果playMusicFromLocal()不在乎延迟和堵塞,那么不需要用线程池包装,直接添加方法即可。

 

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值