必威体育Betway必威体育官网
当前位置:首页 > IT技术

ScheduledExecutorService的使用

时间:2019-11-06 13:14:34来源:IT技术作者:seo实验室小编阅读:53次「手机版」
 

scheduledexecutorservice

前言

Android线程池主要有四个:

newSingleThreadExecutor:单线程池,同时只有一个线程在跑。

newcachedThreadPool() :回收型线程池,可以重复利用之前创建过的线程,运行线程最大数是integer.MAX_VALUE。

newFixedThreadPool() :固定大小的线程池,跟回收型线程池类似,只是可以限制同时运行的线程数量

哎???第四个是什么来着,不知道你有没有想起来,反正我是没记住,结果面试的时候就栽了个跟头。

正文

第四种线程池是scheduledexecutorservice,我平时没有用过,他的最大优点除了线程池的特性以外,可以实现循环或延迟任务。

ScheduledExecutorService 和 Timer 的区别

Timer的内部只有一个线程,如果有多个任务的话就会顺序执行,这样我们的延迟时间和循环时间就会出现问题。

ScheduledExecutorService是线程池,所以就不会出现这个情况,在对延迟任务和循环任务要求严格的时候,就需要考虑使用ScheduledExecutorService了。

ScheduledExecutorService的用法主要有三个:

public class MainActivity extends APPCompatActivity {

    // 通过静态方法创建ScheduledExecutorService的实例
    private ScheduledExecutorService mScheduledExecutorService = Executors.newScheduledThreadPool(4);

    @Override
    protected void onCreate(Bundle savedinstanceState) {
        super.onCreate(savedInstanceState);
        setcontentView(R.layout.activity_main);

        // 延时任务
        mScheduledExecutorService.schedule(threadFactory.newThread(new Runnable() {
            @Override
            public void run() {
                Log.e("lzp", "first task");
            }
        }), 1, TimeUnit.SECONDS);

        // 循环任务,按照上一次任务的发起时间计算下一次任务的开始时间
        mScheduledExecutorService.scheduleatfixedrate(new Runnable() {
            @Override
            public void run() {
                Log.e("lzp", "first:" + system.currenttimemillis() / 1000);
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printstacktrace();
                }
            }
        }, 1, 1, TimeUnit.SECONDS);

        // 循环任务,以上一次任务的结束时间计算下一次任务的开始时间
        mScheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
                Log.e("lzp", "scheduleWithFixedDelay:" + System.currentTimeMillis() / 1000);
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, 1, 1, TimeUnit.SECONDS);


    }
}

代码中已经有注释说明这三个方法的用法,重点是scheduleAtFixedRate和scheduleWithFixedDelay的区别,下面看具体看一下Log信息:

首先是scheduleAtFixedRate:

这里写图片描述

从log上看,我们的循环任务严格按照每一秒发起一次,sleep(3000)对于任务的开启是没有影响的,也就是以上一个任务的开始时间 + 延迟时间 = 下一个任务的开始时间。

然后是scheduleWithFixedDelay:

这里写图片描述

从log上看,每一个任务的时间间隔是4秒,而不是我们设置的间隔1秒,任务要耗时3秒,两个时间相加正好是4秒,那么之前代码注释的解释就说的通了:以上一次任务的结束时间 + 延迟时间 = 下一次任务的开始时间。

扩展:ThreadFactory

因为在线程池的构造方法中,有ThreadFactory参数,所以就来简单介绍一个ThreadFactory:

线程工厂,可以对传入的Runnable进行加工操作,典型的工厂模式,需要实现newThread(@NonNull Runnable r) 方法返回加工后的线程。

看一下具体的代码:

private ThreadFactory threadFactory = new ThreadFactory() {
        @Override
        public Thread newThread(@NonNull final Runnable r) {
            return new Thread() {
                @Override
                public void run() {
                    r.run();
                    Log.e("lzp", "嘿嘿嘿");
                }
            };
        }
    };

例如上面的代码,我在参数Runnable对象的run方法后,增加了一句log打印。

然后修改MainActivity的调用:

mScheduledExecutorService.schedule(threadFactory.newThread(new Runnable() {
            @Override
            public void run() {
                Log.e("lzp", "first task");
            }
        }), 1, TimeUnit.SECONDS);

这里面我传入ThreadFactory加工过后的Runnable,我们推断,最后应该会打印两句:first task 和 嘿嘿嘿,运行一下看看是不是符合我们的期望:

这里写图片描述

果然结果没有让我们失望。

然后我有了一个想法,我要给线程池所有的线程都加工一下:

private ScheduledExecutorService mScheduledExecutorService = Executors.newScheduledThreadPool(4, new ThreadFactory() {
        @Override
        public Thread newThread(@NonNull Runnable r) {
           return new Thread(r){
                @Override
                public void run() {
                    Log.e("lzp", "newThread");
                    super.run();
                    Log.e("lzp", "newThread over");
                }
            };
        }
    });

我在Runnable参数的run方法前后增加了打印,于是我迫不及待的运行了app:

这里写图片描述

经过短暂的喜悦,我发现我的希望落空了,出现了两个问题:

1、Log.e(“lzp”, “newThread over”) 这一句为什么没有执行???

2、为什么ThreadFactory.newThread() 只执行了四次。

带着疑问我开始查看源码,经过仔细的思考,找到了问题的原因,这里就简单解释一下:

1、线程池只会创建最大并发数(corePoolSize)的Worker对象。

2、Worker对象内部包含了Thread和Runnable信息。

3、添加任务时:

如果Worker没有到最大值,就创建Worker(通过ThreadFactory的newThread新建了Thread,所以Thread的数量等于Worker数量);

如果已经是最大,就把任务放到队列(Quene)中。

3、任务执行中:

如果Worker的Runnable不为空,执行Runnable,执行结束,Runnable设置为空;

如果Worker的Runnable为空,从队列中取出任务执行。

4、Runnable运行结束,线程池内部直接退出了线程。

弄懂了内部的机制,刚才的问题就全都弄明白了,还发现android的设计者是不希望我们通过这种方法去影响Runnable的实现方法,但是可以去改变线程Thread对象的属性,例如优先级等等。

总结

这一篇我们先去弄懂了ScheduledExecutorService的特性和用法,然后了解了ThreadFactory的用法,最后两者结合使用,分析并解决可能出现的问题。

由于线程池的源码还是很多的,这里就不具体的解释了,想具体了解的朋友可以自己去查看源码或者网上查看资料。

最后带上源码链接,有问题或者建议可以留言。

Demo下载链接

转自: https://blog.csdn.net/u011315960/article/details/71422386

文章最后发布于: 2018-09-27 10:30:20

相关阅读

ScheduledExecutorService使用之——重复创建停止周期

ScheduledExecutorService的创建就不用多说,直接上代码 executorService = Executors.newSingleThreadScheduledExecutor(); Sch

ScheduledExecutorService 延迟/周期执行线程池

目录 Executor 结构图 ScheduleExecutorService 简述 对象创建方式 方法详解 schedule Runnable  schedule Callable scheduleAt

ScheduledExecutorService定时周期执行指定的任务

一:简单说明ScheduleExecutorService接口中有四个重要的方法,其中scheduleAtFixedRate和scheduleWithFixedDelay在实现定时程序时比

ScheduledExecutorService:多线程任务调度

今天使用Timer实现任务调度时,阿里巴巴Java开发规范提示 多线程并行处理定时任务时,Timer运行多个TimeTask时,只要其中之一没有捕获

分享到:

栏目导航

推荐阅读

热门阅读