你想要的线程池知识,都在这里

对于android程序员来说,学会管理线程,显得尤为重要,我们面对的是手机,这就更应该注意效能的开销,而当遇到复杂的业务逻辑需要开启多个线程去处理时,学会使用线程池,就会变得尤为重要。

1.线程池的优点

(1)我们可以通过简单的参数配置,实现重用已经创建好的线程,避免频繁创建而导致的频繁GC。

(2)依然是简单的参数配置,就可以控制线程的最大并发数,避免大量线程抢夺系统资源而导致的阻塞现象

(3)直接调用线程池中的方法,对整体线程进行简单的管理,例如定时执行或者指定间隔循环执行。


2.ThreadPoolExecutor

线程池中,一个最基础的类就是ThreadPoolExecutor,它是对线程池真正的实现,我们来看一下它的构造方法:

	public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) 
我们来对这些参数一一讲解:

(1)corePoolSize:核心线程数,它的意义在于,当我们提交任务想让线程池执行时,只要任务的数量没有达到该数目,就会毫不犹豫的去开启一个线程去执行,直到开启的线程数数量达到我们指定的corePoolSize,核心线程一旦开启,即使处于闲置状态,也会一直存活,当然,核心线程的存活时间也是可控的,我们可以调用allowCoreThreadTimeOut方法,参数传true,然后指定第三个参数keepAliveTime,那么核心线程闲置时间超过keepAliveTime,就会被终止。

(2)maximumPoolSize:线程池所能容纳的最大线程数,也就是说当提交任务后,核心线程数开启的数量达到corePoolSize,workQueue中的的任务数也达到指定的数量,那么这时如果maximumPoolSize的数值大于coorPoolSize,那么就会继续开启线程来执行任务,直到开启的线程总数量等于maximumPoolSize。如果继续提交任务,那么就会被阻塞。

(3)keepAliveTime:非核心线程闲置时的超时时长,就是指的当非核心线程开启后,如果闲置下来的时间超过这个时长,就会被回收,当然根据(1)中所说,通过指定属性,这个参数同样可以作用于核心线程。

(4)unit:用来指定keepAliveTime的时间单位,以下是所有提供好的时间单位:

	TimeUnit.DAYS(天)	
	TimeUnit.HOURS(小时)
	TimeUnit.MINUTES(分钟)
	TimeUnit.SECONDS(秒)
	TimeUnit.MILLISECONDS(毫秒)
	TimeUnit.MICROSECONDS(微秒)
	TimeUnit.NANOSECONDS(纳秒)

(5)workQueue:任务队列,用于存储线程没有处理但是已经提交的任务,BlockingQueue是一个特殊的队列,如果取数据时队列为空,那么就会阻塞,如果队列中有数据,就会被重新唤醒。另外存储数据时,队列已满也会阻塞,直到有空间后会被重新唤醒,常用的实现类有以下几种

	------ArrayBlockingQueue:表示一个规定大小的BlockingQueue,构造函数接受一个int类型的数据,用来指定大小,遵循先进先出原则。

	------LinkedBlockingQueue:表示一个大小不确定的BlockingQueue,如果在构造方法中传入值,那就是指定了大小,如果没有,就代表大小为Integer.MAX_VALUE。

	------PriorityBlockingQueue:和LinkedBlockingQueue相似,但是取出的循序按照元素的Comparator来决定。

	------SynchronousQueue:一个很特殊的同步队列,元素只有在试图取走的时候才有可能存在,也就是说如果想要在该队列中插入元素,必须提前有试图取出元素的动作,可以说我们可以理解为生产者和消费者互相等待,等到对方之后然后再一起离开。

(6)另外还有其他的构造方法,其中还有两个参数,一个是threadFactory,为线程池提供创建新线程的功能,我们一般不用管,默认即可,还有一个是handler,也就是一个拒绝策略,当提交的任务无法执行并且也无法存储到任务队列时,会抛出RejectedExecutionException,我们在这里也是经常使用其默认的。


3.线程池执行时遵循的规则

(1)线程池中的总线程数量,未达到核心线程数量,也就是未达到第一个参数corePoolSize的数量,直接开启一个核心线程去执行。

(2)如果线程池中的总线程数量达到或者超过核心线程数量,并且这时队列仍有空间,任务会被存储到队列中等待执行。

(3)如果线程池中的总线程数量达到或者超过核心线程数量,并且这时队列此时已满,而总的线程数量并没有达到第二个参数maximumPoolSize指定的线程池所能容纳的最大线程数,这时会开启一个非核心线程去执行。

(4)而当线程池中的总数量如果线程池中的总线程数量达到或者超过核心线程数量,并且这时队列此时已满,而总的线程数量也已经达到或者超过线程池所能容纳的最大线程数,这时会直接抛出RejectedExecutionException。


4.举例验证执行规则

首先来看第一个规则和第二个规则:

package com.example.hewenqiang.thread;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class PoolActivity extends AppCompatActivity {
    private static final String FLAG = "heweniqang";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pool);
        ThreadPoolExecutor executor =
                new ThreadPoolExecutor(3, 6, 3, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(20));
        for (int x = 0; x < 20; x++) {
            final int i = x;
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(2000);
                        Log.i(FLAG, "执行完毕" + i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }

    }
}
再看Log日志

04-06 04:47:22.094 1652-1665/com.example.hewenqiang.thread I/heweniqang: 执行完毕0
04-06 04:47:22.100 1652-1666/com.example.hewenqiang.thread I/heweniqang: 执行完毕1
04-06 04:47:22.101 1652-1667/com.example.hewenqiang.thread I/heweniqang: 执行完毕2
04-06 04:47:24.094 1652-1665/com.example.hewenqiang.thread I/heweniqang: 执行完毕3
04-06 04:47:24.101 1652-1666/com.example.hewenqiang.thread I/heweniqang: 执行完毕4
04-06 04:47:24.101 1652-1667/com.example.hewenqiang.thread I/heweniqang: 执行完毕5
04-06 04:47:26.094 1652-1665/com.example.hewenqiang.thread I/heweniqang: 执行完毕6
04-06 04:47:26.103 1652-1666/com.example.hewenqiang.thread I/heweniqang: 执行完毕7
04-06 04:47:26.103 1652-1667/com.example.hewenqiang.thread I/heweniqang: 执行完毕8
04-06 04:47:28.105 1652-1665/com.example.hewenqiang.thread I/heweniqang: 执行完毕9
04-06 04:47:28.105 1652-1667/com.example.hewenqiang.thread I/heweniqang: 执行完毕11
04-06 04:47:28.105 1652-1666/com.example.hewenqiang.thread I/heweniqang: 执行完毕10
04-06 04:47:30.105 1652-1666/com.example.hewenqiang.thread I/heweniqang: 执行完毕14
04-06 04:47:30.105 1652-1665/com.example.hewenqiang.thread I/heweniqang: 执行完毕12
04-06 04:47:30.105 1652-1667/com.example.hewenqiang.thread I/heweniqang: 执行完毕13
04-06 04:47:32.106 1652-1667/com.example.hewenqiang.thread I/heweniqang: 执行完毕17
04-06 04:47:32.107 1652-1666/com.example.hewenqiang.thread I/heweniqang: 执行完毕16
04-06 04:47:32.107 1652-1665/com.example.hewenqiang.thread I/heweniqang: 执行完毕15
04-06 04:47:34.108 1652-1667/com.example.hewenqiang.thread I/heweniqang: 执行完毕18
04-06 04:47:34.108 1652-1666/com.example.hewenqiang.thread I/heweniqang: 执行完毕19

这里没有gif图,我来描述一下执行过程,首先会开启三个线程去执行任务,然后之后提交的会放入队列中,然后如果有任务执行完毕,会从队列中取出任务然后执行,该线程池中始终只有三个线程,接下来我们把配置参数修改一下

package com.example.hewenqiang.thread;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class PoolActivity extends AppCompatActivity {
    private static final String FLAG = "heweniqang";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pool);
        ThreadPoolExecutor executor =
                new ThreadPoolExecutor(3, 20, 3, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3));
        for (int x = 0; x < 20; x++) {
            final int i = x;
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(2000);
                        Log.i(FLAG, "执行完毕" + i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }

    }
}

我们来看log:

04-06 04:57:09.259 10458-10471/com.example.hewenqiang.thread I/heweniqang: 执行完毕0
04-06 04:57:09.260 10458-10472/com.example.hewenqiang.thread I/heweniqang: 执行完毕1
04-06 04:57:09.260 10458-10473/com.example.hewenqiang.thread I/heweniqang: 执行完毕2
04-06 04:57:09.264 10458-10474/com.example.hewenqiang.thread I/heweniqang: 执行完毕6
04-06 04:57:09.264 10458-10475/com.example.hewenqiang.thread I/heweniqang: 执行完毕7
04-06 04:57:09.265 10458-10476/com.example.hewenqiang.thread I/heweniqang: 执行完毕8
04-06 04:57:09.274 10458-10477/com.example.hewenqiang.thread I/heweniqang: 执行完毕9
04-06 04:57:09.275 10458-10480/com.example.hewenqiang.thread I/heweniqang: 执行完毕12
04-06 04:57:09.275 10458-10478/com.example.hewenqiang.thread I/heweniqang: 执行完毕10
04-06 04:57:09.276 10458-10479/com.example.hewenqiang.thread I/heweniqang: 执行完毕11
04-06 04:57:09.277 10458-10481/com.example.hewenqiang.thread I/heweniqang: 执行完毕13
04-06 04:57:09.280 10458-10483/com.example.hewenqiang.thread I/heweniqang: 执行完毕15
04-06 04:57:09.281 10458-10482/com.example.hewenqiang.thread I/heweniqang: 执行完毕14
04-06 04:57:09.284 10458-10484/com.example.hewenqiang.thread I/heweniqang: 执行完毕16
04-06 04:57:09.286 10458-10485/com.example.hewenqiang.thread I/heweniqang: 执行完毕17
04-06 04:57:09.287 10458-10487/com.example.hewenqiang.thread I/heweniqang: 执行完毕19
04-06 04:57:09.291 10458-10486/com.example.hewenqiang.thread I/heweniqang: 执行完毕18
04-06 04:57:11.260 10458-10471/com.example.hewenqiang.thread I/heweniqang: 执行完毕3
04-06 04:57:11.260 10458-10472/com.example.hewenqiang.thread I/heweniqang: 执行完毕4
04-06 04:57:11.260 10458-10473/com.example.hewenqiang.thread I/heweniqang: 执行完毕5

我们可以清楚的看到Log信息,首先开启三个核心线程去执行任务,然后提交的任务会存到队列中,而队列的容量为3,那么从第六个任务开始,会开启非核心线程去执行,然后最后一个任务执行完毕后,会再从队列中取出存储的任务然后执行。这也就验证了第三个规则。

最后我们来验证一下最后一个规则,也是修改一下参数:

package com.example.hewenqiang.thread;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class PoolActivity extends AppCompatActivity {
    private static final String FLAG = "heweniqang";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pool);
        ThreadPoolExecutor executor =
                new ThreadPoolExecutor(3, 6, 3, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3));
        for (int x = 0; x < 20; x++) {
            final int i = x;
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(2000);
                        Log.i(FLAG, "执行完毕" + i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }

    }
}
看一下Log日志(仅贴出主要的):

......
        Caused by: java.util.concurrent.RejectedExecutionException: 

......
04-06 05:03:44.570 16396-16413/com.example.hewenqiang.thread I/heweniqang: 执行完毕6
04-06 05:03:44.570 16396-16414/com.example.hewenqiang.thread I/heweniqang: 执行完毕7
04-06 05:03:44.572 16396-16412/com.example.hewenqiang.thread I/heweniqang: 执行完毕2
04-06 05:03:44.573 16396-16410/com.example.hewenqiang.thread I/heweniqang: 执行完毕0
04-06 05:03:44.577 16396-16411/com.example.hewenqiang.thread I/heweniqang: 执行完毕1
04-06 05:03:44.578 16396-16415/com.example.hewenqiang.thread I/heweniqang: 执行完毕8
04-06 05:03:46.612 16396-16413/com.example.hewenqiang.thread I/heweniqang: 执行完毕3
04-06 05:03:46.612 16396-16414/com.example.hewenqiang.thread I/heweniqang: 执行完毕4
04-06 05:03:46.612 16396-16412/com.example.hewenqiang.thread I/heweniqang: 执行完毕5

我们可以看到,只有前九个任务执行了,也就是说首先开启三个核心线程去执行三个任务,接下来的三个任务,存储到队列中,然后再提交的三个任务会开启非核心线程去执行,然而继续提交的任务,才是线程池中的线程数已经达到所能容纳的最大值,这是就会抛出异常,那么最后仅仅执行了前九个任务。


5.参数配置参考

当然在实际开发中,我们一般不会一下开启30个线程,而去根据手机的性能,去发挥最大的处理能力,这里我们可以参考一下AsyncTask的源码,他为我们提供了非常好的参考,也就是根据手机性能去配置参数,从而最大限度的并且合理的去利用资源

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;

private static final ThreadFactory sThreadFactory = new ThreadFactory() {
    private final AtomicInteger mCount = new AtomicInteger(1);

    public Thread newThread(Runnable r) {
        return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
    }
};

private static final BlockingQueue<Runnable> sPoolWorkQueue =
        new LinkedBlockingQueue<Runnable>(128);

/**
 * An {@link Executor} that can be used to execute tasks in parallel.
 */
public static final Executor THREAD_POOL_EXECUTOR;

static {
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
            sPoolWorkQueue, sThreadFactory);
    threadPoolExecutor.allowCoreThreadTimeOut(true);
    THREAD_POOL_EXECUTOR = threadPoolExecutor;
}

我们能够看到,核心线程数配置是根据手机cpu数量进行配置,最少为两个,最多也不超过4个,而最大线程数也是根据手机cpu数量来制定,我们在日常配置时,可以参考。


6.java中四个封装好的线程池

(1)FixedThreadPool:是一个核心线程数量固定的线程池,该线程池获取的方式为

ExecutorService executorService = Executors.newFixedThreadPool(3);
我们先来看一下源码

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}
我们能够看到,核心线程数和最大线程数是一样的,也就是说这个线程池中不允许非核心线程的存在,而队列的大小没有指定,那么大小就是Integer.MAX_VALUE,并且我们能够看到超时时间为0,这也就说明该线程池中的核心线程一旦都开启,即使没有任何任务,线程也不会销毁,保证最快的响应速度,而当核心线程在执行任务时,新提交的任务会存储的队列中,直到线程空闲出来。我们来看个Demo

package com.example.hewenqiang.thread;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class FixedActivity extends AppCompatActivity {
    private static final String FLAG = "heweniqang";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fixed);
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        for (int x = 0; x < 30; x++) {
            final int i = x;
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(500);
                        Log.i(FLAG, "执行完毕:" + i);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }
}

再来看一下Log:

04-06 23:00:45.183 2318-2338/com.example.hewenqiang.thread I/heweniqang: 执行完毕:1
04-06 23:00:45.197 2318-2339/com.example.hewenqiang.thread I/heweniqang: 执行完毕:2
04-06 23:00:45.215 2318-2337/com.example.hewenqiang.thread I/heweniqang: 执行完毕:0
04-06 23:00:45.685 2318-2338/com.example.hewenqiang.thread I/heweniqang: 执行完毕:3
04-06 23:00:45.697 2318-2339/com.example.hewenqiang.thread I/heweniqang: 执行完毕:4
04-06 23:00:45.715 2318-2337/com.example.hewenqiang.thread I/heweniqang: 执行完毕:5
04-06 23:00:46.185 2318-2338/com.example.hewenqiang.thread I/heweniqang: 执行完毕:6
04-06 23:00:46.198 2318-2339/com.example.hewenqiang.thread I/heweniqang: 执行完毕:7
04-06 23:00:46.216 2318-2337/com.example.hewenqiang.thread I/heweniqang: 执行完毕:8
04-06 23:00:46.686 2318-2338/com.example.hewenqiang.thread I/heweniqang: 执行完毕:9
04-06 23:00:46.698 2318-2339/com.example.hewenqiang.thread I/heweniqang: 执行完毕:10
04-06 23:00:46.717 2318-2337/com.example.hewenqiang.thread I/heweniqang: 执行完毕:11
04-06 23:00:47.187 2318-2338/com.example.hewenqiang.thread I/heweniqang: 执行完毕:12
04-06 23:00:47.199 2318-2339/com.example.hewenqiang.thread I/heweniqang: 执行完毕:13
04-06 23:00:47.217 2318-2337/com.example.hewenqiang.thread I/heweniqang: 执行完毕:14
04-06 23:00:47.696 2318-2338/com.example.hewenqiang.thread I/heweniqang: 执行完毕:15
04-06 23:00:47.699 2318-2339/com.example.hewenqiang.thread I/heweniqang: 执行完毕:16
04-06 23:00:47.717 2318-2337/com.example.hewenqiang.thread I/heweniqang: 执行完毕:17
04-06 23:00:48.196 2318-2338/com.example.hewenqiang.thread I/heweniqang: 执行完毕:18
04-06 23:00:48.199 2318-2339/com.example.hewenqiang.thread I/heweniqang: 执行完毕:19
04-06 23:00:48.226 2318-2337/com.example.hewenqiang.thread I/heweniqang: 执行完毕:20
04-06 23:00:48.697 2318-2338/com.example.hewenqiang.thread I/heweniqang: 执行完毕:21
04-06 23:00:48.700 2318-2339/com.example.hewenqiang.thread I/heweniqang: 执行完毕:22
04-06 23:00:48.736 2318-2337/com.example.hewenqiang.thread I/heweniqang: 执行完毕:23
04-06 23:00:49.205 2318-2338/com.example.hewenqiang.thread I/heweniqang: 执行完毕:24
04-06 23:00:49.205 2318-2339/com.example.hewenqiang.thread I/heweniqang: 执行完毕:25
04-06 23:00:49.248 2318-2337/com.example.hewenqiang.thread I/heweniqang: 执行完毕:26
04-06 23:00:49.706 2318-2338/com.example.hewenqiang.thread I/heweniqang: 执行完毕:27
04-06 23:00:49.706 2318-2339/com.example.hewenqiang.thread I/heweniqang: 执行完毕:28
04-06 23:00:49.755 2318-2337/com.example.hewenqiang.thread I/heweniqang: 执行完毕:29

很简单,我们如果需要维护一个固定数量的线程池,并且需要很快的响应速度,这是一个很好的选择。

(2)SingleThreadExecutor:当然还是通过Executors类中的静态方法newSingleThreadExecutor来创建,首先看一下源码

public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
可以很清楚的看到,这是一个只维护一个核心线程的线程池,确保所有任务都在同一个线程中按顺序执行,不需要我们去处理线程同步问题,demo就不再给出了,因为这个线程池很好理解。

(3)CachedThreadPool:创建方式依然通过Executors类,然后通过newCachedThreadPool方法来创建,先看源码

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}
可以看到核心线程数为0,最大线程数位Integer.MAX_VALUE,而这里采用的队列也很特殊,就是我们之前所讲的,这个队列实际上是不能存储任何任务的,也就是说这个线程池只要提交任务就会立即开启线程处理,要么就利用闲置下来的线程处理,当然如果这个线程闲置时间超过60秒,就会被回收,那么这个线程池十分适合处理大量的但是每个任务耗时比较的短的任务。

(4)ScheduledThreadPool:通过Executors的newScheduledThreadPool,我们也来看源码:

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());
}
核心线程固定,但是非核心线程数可以非常大,非核心线程一旦闲置立马回收,这类线程适合用于执行定时任务和具有固定周期的任务,我们可以看到,这里有一个接口,就是ScheduledExecutorService,这是一个继承于ExecutorService接口,而这个接口定义了执行定时任务和具有固定周期任务的方法

我们来看一下其中的几个方法

------schedule:

public ScheduledFuture<?> schedule(Runnable command,
                                   long delay, TimeUnit unit);
该方法的作用是延迟启动任务,通过指定参数,可以按照我们指定的时间以后启动任务

------scheduleAtFixedRate:

public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                              long initialDelay,
                                              long period,
                                              TimeUnit unit);
该方法可以延时并且定时执行任务,也就是延迟initialDelay时间后每隔period时间执行一次任务

------scheduleWithFixedDelay:

public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                 long initialDelay,
                                                 long delay,
                                                 TimeUnit unit);

该方法的作用于上个方法相同,但是在这里我要说明一下,这些间隔作用的是线程,也就是说如果一次间隔执行的所有任务在很短的时间内就执行完毕,我们能清楚的看到根据我们指定的间隔时间然后去反复执行任务,而当任务量非常大,单次执行完成所需的任务时间已经超过了间隔时间,那么我们就感知不到间隔的时间,因为下一次间隔的时间中间,还有任务没有执行完毕,所以间隔时间我们就无法感知。


7.线程池中的常用方法:

(1)shutDown():关闭线程池,但不影响已经提交的任务。

(2)shutDownNow():关闭线程池,并尝试终止正在执行的线程。这里我想展开说一下,这里终止线程,底层运用的依然是interrupt,我们要明确该方法,该方法最确切的来说是中断一个被阻塞的线程,那什么情况下线程阻塞呢?一般来说就是在线程wait,sleep,或者join的时候,线程处于阻塞状态,这时如果调用interrupt方法,该线程会被终止,并抛出InterruptedException异常。这个方法,是不会中断一个正在运行的线程,那么真正想中断一个正在运行的线程,还是需要通过标记值,也就是说这个任务在循环执行的时候,通过外接的boolean标记,去控制线程中的任务是否继续执行。

(3)allowCoreThreadTimeOut(boolean value):如果传入true,就意味着允许核心线程闲置超时的时候被回收。

(4)isShutDown():当调用shutDown()方法后返回true。

(5)isTerminated();当调用shutDown()方法后,并且线程池中的任务都执行完成后,返回true。(注意前提是调用了shutDown()方法)

(4)submit:这也是提交任务的一种方法,如果不需要返回值,我们直接使用execute,如果需要返回值,就要利用该方法。熟悉Callable开启线程的同学们应该对有返回值的线程不陌生,线程池只是对其做了一下封装,不熟悉也没关系,我们来看个例子:

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import java.util.ArrayList;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadPoolCallableActivity extends AppCompatActivity {
    private boolean isStop;
    private ThreadPoolExecutor threadPoolExecutor;
    private ArrayList<Future<String>> list;
    private static final String FLAG = "hewenqiang";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_thread_pool_callable);
        threadPoolExecutor = new ThreadPoolExecutor(3, 6, 3, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());

        //submit方式提交任务后,线程的返回值,会被封装到Future里,所以这里先来创建一个集合,用来存储Future
        list = new ArrayList<>();
        //创建一个类实现Callable接口
        for (int x = 0; x < 9; x++) {
            MyTask myTask = new MyTask(x);
            Future<String> future = threadPoolExecutor.submit(myTask);
            list.add(future);
        }
        threadPoolExecutor.shutdown();
        //创建一个SingleThread线程池,用于每隔1秒检测一下所有的任务是否完成,如果完成停止循环
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                while (!isStop) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //如果任务完成,我们获取所有任务的返回值
                    if (threadPoolExecutor.isTerminated()) {
                        for (Future<String> future : list) {
                            try {
                                Log.i(FLAG, future.get());
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            } catch (ExecutionException e) {
                                e.printStackTrace();
                            }
                        }
                        isStop = true;
                    }
                }
            }
        });
    }

    class MyTask implements Callable<String> {
        private int taskId;

        public MyTask(int taskId) {
            this.taskId = taskId;
        }

        @Override
        public String call() throws Exception {
            Thread.sleep(3000);
            return "Thread:" + taskId + " 执行完毕";
        }
    }
}

代码注释很清楚,简述一下逻辑,首先开启了一个线程池,然后提交10个任务去执行,然后另外开启一个线程池,用来查看之前的线程池中的任务是否执行完毕,其中运用的方法,之前也讲解过,然后执行完毕后,获取10个任务的返回值。

到此,关于线程池常用的知识介绍完毕,对于android来说,一旦涉及到复杂业务逻辑需要用到线程的,要优先考虑线程池,因为我们面对的是移动设备,所以对于系统资源既要充分利用,也要尽量节省,希望本篇博客对您有所帮助,当然不足之处希望指正。






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值