android 线程池

作者: 马德率的伊比 | 来源:发表于2017-06-05 17:54 被阅读0次

    先来看一看ThreadPoolExecutor的一个常用的构造方法。

    public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
                                  long keepAliveTime,
                                  TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue) {
            ...
        }
    

    看这构造器,一眼看上去,哇(wo)塞(cao)。这个构造器算少的了,其实每个参数非常好理解,这里简单说一下吧。

    • int corePoolSize表示核心线程数,在不设置allowCoreThreadTimeOut为ture的情况下,核心线程就算没事做也不会被销毁。
    • int maximumPoolSize最大线程数。
    • long keepAliveTime超时时长,一个非核心线程(设置allowCoreThreadTimeOut为ture也同样作用于核心线程)在处于闲置状态(没事做)超过这个时长就会被销毁。
    • TimeUnit unit时间单位,有秒、毫秒、微秒...等。
    • BlockingQueue<Runnable> workQueue缓存任务队列。

    这里就不过多的介绍参数的意义,需要的可以自行去查查,这里着重来看看常用的4个线程池的基本用法。

    可缓存线程池 CachedThreadPool

    先看下源码

    public static ExecutorService newCachedThreadPool() {
            return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                          60L, TimeUnit.SECONDS,
                                          new SynchronousQueue<Runnable>());
        }
    

    根据源码可以看出:

    1. 这种线程池内部没有核心线程,线程的数量是有没限制的。
    2. 在创建任务时,若有空闲的线程时则复用空闲的线程,若没有则新建线程。
    3. 没有工作的线程(闲置状态)在超过了60S还不做事,就会销毁。
      我会的专业术语就会这点了(口水咽下,请别喷....),下面直接看例子吧。
    buju.png

    这是整个布局界面,简直low的不行就不多说了,一眼就看裸了。

    mCachedThreadPool = Executors.newCachedThreadPool();//创建可缓存线程池
    
    //开始下载
    private void startDownload(final ProgressBar progressBar, final int i) {
            mCachedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                    int p = 0;
                    progressBar.setMax(10);//每个下载任务10秒
                    while (p < 10) {
                        p++;
                        progressBar.setProgress(p);
                        Bundle bundle = new Bundle();
                        Message message = new Message();
                        bundle.putInt("p", p);
                        //把当前线程的名字用handler让textview显示出来
                        bundle.putString("ThreadName", Thread.currentThread().getName());
                        message.what = i;
                        message.setData(bundle);
                        mHandler.sendMessage(message);
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
        }
    

    上面这段代码.....没有什么难度和技术。我们先下载A,在A还没有下载完成的时候再下载B,可以看到是这样的。

    1.png

    看textview后面显示的Thread名字,“thread-1”和“thread-2”很明显不是同一个线程。当A下载完成之后(60S)内,然后下载C(图中B也还没有下载完成)。

    2.png

    看到下载C任务的线程时“thread-1”。复用了之前空闲的线程,省去了创建线程的时间。接下来当ABC都下载好了60S以后(当然你可以自己创建一个线程池把60S改小些)。再去下载D和E。

    3.png

    现在就是线程“thread-3”“thread-4”创建了新的线程,说明之前的线程因为超过了设置的时间不干活被炒了。

    FixedThreadPool 定长线程池

    老规矩先帖下源码

     public static ExecutorService newFixedThreadPool(int nThreads) {
            return new ThreadPoolExecutor(nThreads, nThreads,
                                          0L, TimeUnit.MILLISECONDS,
                                          new LinkedBlockingQueue<Runnable>());
        }
    

    根据源码

    1. 该线程池的最大线程数等于核心线程数,所以在默认情况下,该线程池的线程不会因为闲置状态超时而被销毁。
    2. 如果当前线程数小于核心线程数,并且也有闲置线程的时候提交了任务,这时也不会去复用之前的闲置线程,会创建新的线程去执行任务。如果当前执行任务数大于了核心线程数,大于的部分就会进入队列等待。等着有闲置的线程来执行这个任务。
      看下例子,例子大部分还是上面的例子,只是修改了一下,首先创建线程池
    mFixedThreadPool = Executors.newFixedThreadPool(3);//创建定长线程池
    

    这个设置了3个核心线程数。然后把上面startDownload方法修改一下

      private void startDownload(final ProgressBar progressBar, final int i) {
            mFixedThreadPool.execute(new Runnable() {
                @Override
                public void run() {
                   //....逻辑代码和上面一样
                }
            });
        }
    

    现在先下载A,等到A下载完成再下载B,会看到下面结果

    1.png

    可以看到现在即使是“thread-1”处于闲置状态,再开始B任务,也不会复用“thread-1”。会创建一个新的“thread-2”新的线程来执行任务。
    接下来我们下载C,这时又会创建一个新的线程“thread-3”来执行任务。现在我们设置的3个线程数已经有了,接下来再下载D。

    2.png

    这个时候是“thread-1”来执行的任务。也就是说当我们现在的核心线程数达到我们设置的线程数之后,再来执行任务,如果线程池中有闲置的线程,就会复用之前闲置的线程来执行任务。如果现在要执行的任务超过了现在可用的线程,那么超过的任务就会进入队列等待。现在我们重启一下程序,直接点击全部下载。

    3.gif

    点击全部下载之后,由于我们只设置了3个线程数,所以DE就加入了等待队列,等到前边下载完成空闲了之后才开始执行DE任务。

    SingleThreadPool

     public static ExecutorService newSingleThreadExecutor() {
            return new FinalizableDelegatedExecutorService
                (new ThreadPoolExecutor(1, 1,
                                        0L, TimeUnit.MILLISECONDS,
                                        new LinkedBlockingQueue<Runnable>()));
        }
    

    这个线程池很明显了吧,就只有一个线程,所有的任务都遵循入队出队规则。这个线程池就不演示了。

    ScheduledThreadPool

    老规矩,98号...啊呸...上源码

     public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
            return new ScheduledThreadPoolExecutor(corePoolSize);
        }
    
    public ScheduledThreadPoolExecutor(int corePoolSize) {
            super(corePoolSize, Integer.MAX_VALUE,
                  DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
                  new DelayedWorkQueue());
        }
    

    DEFAULT_KEEPALIVE_MILLIS就是默认10L,这里就是10秒。这个线程池有点像是吧CachedThreadPool和FixedThreadPool 结合了一下。

    1. 不仅设置了核心线程数,最大线程数也是Integer.MAX_VALUE。
    2. 这个线程池是上述4个中为唯一个有延迟执行和周期执行任务的线程池。

    首先创建线程池

     mScheduledThreadPool = Executors.newScheduledThreadPool(3);
    

    一般的执行任务方法和上面的都大同小异,我们主要看看延时执行任务和周期执行任务的方法。

    mScheduledThreadPool.schedule(new Runnable() {
                @Override
                public void run() {
                //....
                }
            }, 3, TimeUnit.SECONDS);
    

    这个方法一看就知道,表示在3秒之后开始执行我们的任务。

    mScheduledThreadPool.scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
                 //....
                }
            },3, 7, TimeUnit.SECONDS);
    
    mScheduledThreadPool.scheduleWithFixedDelay(new Runnable() {
                @Override
                public void run() {
                 //....
                }
            },3, 7, TimeUnit.SECONDS);
    

    咦?上面两个一毛一样?注(yan)意(xia)看(a)。两个方法的方法名都不一样,参数是相同的。意思上也都是延迟3秒之后开始执行任务,每隔7秒执行一次。这尼玛一样啊,玩珠珠啊。别急,这里我就直接给出两个方法的不同,有兴趣的可以去自己试试。

    • 第一个方法是延迟3秒后执行任务,从开始执行任务这个时候开始计时,每7秒执行一次不管执行任务需要多长的时间。
    • 第二个方法是延迟3秒后执行任务,从任务完成时这个时候开始计时,7秒后再执行,再等完成后计时7秒再执行也就是说这里的循环执行任务的时间点是从上一个任务完成的时候。

    常用的4个线程池的基本用法都说的差不多了,在各位老司机面前耍关公了。第一次.....难免有些紧张,表现不好请各位客官见谅。

    相关文章

      网友评论

        本文标题:android 线程池

        本文链接:https://www.haomeiwen.com/subject/uwrifxtx.html