一,写在前面
线程池的好处:可以在设定固定数量的线程池,并重用线程池中的线程,避免了线程不断的创建和销毁带来的性能开销。另外,还可以定时以及间隔循环执行任务。
Android线程池有哪些:FixedThreadPool,SingleThreadPool,CacheThreadPool,ScheduledThreadPool。事实上,前面列出的四种线程池并不是类,是对线程池的描述。具体来说,FixedThreadPool是固定大小线程池,SingleThreadPool是单任务线程池,CacheThreadPool非固定大小线程池,ScheduledThreadPool是具有定时,间隔循环执行任务的线程池。
什么是线程池?Android线程池是由Java提供的原生API支持,代表线程池的是一个接口Executor,它只有一个execute方法。有一些子类:ThreadPoolExecutor,AbstractExecutorService;有一些子接口:ScheduledExecutorService,ExecutorService。
接口Executor真正实现类是ThreadPoolExecutor,上面提到的四种线程池实质都是ThreadPoolExecutor,只是创建ThreadPoolExecutor对象时传入的参数值不同。因此,研究常用四种线程池前需要认识ThreadPoolExecutor,下面会重点研究。
二,ThreadPoolExecutor分析
查看接口Executor源码:
public interface Executor {
/**
* Executes the given command at some time in the future. The command
* may execute in a new thread, in a pooled thread, or in the calling
* thread, at the discretion of the {@code Executor} implementation.
*
* @param command the runnable task
* @throws RejectedExecutionException if this task cannot be
* accepted for execution
* @throws NullPointerException if command is null
*/
void execute(Runnable command);
}
第13行,定义了一个execute方法,子类ThreadPoolExecutor会重写该方法,参数command指被执行的任务。
查看ThreadPoolExecutor构造方法,主要研究其参数,源码如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler){
//...code
}
corePoolSize:核心线程的数量。核心线程在默认情况下,没有闲置状态的超时机制,不会被系统回收。如果调用ThreadPoolExecutor$allowCoreThreadTimeOut(true),设置属性allowCoreThreadTimeOut值为true,那么核心线程在闲置状态时有超时机制,可以被回收。
maximumPoolSize:线程池中允许的线程最大数量;
keepAliveTime:超时的时间。默认情况指,非核心线程处于闲置的时间超过keepAliveTime,那么非核心线程会被回收。如果希望这个超时机制作用于核心线程,可以设置属性allowCoreThreadTimeOut值为true。
unit:枚举类,时间单位;
workQueue:任务队列。线程池的execute方法中Runnable对象,会被存储到任务队列中。
threadFactory:线程工程。ThreadFactory是一个接口,只有一个newThread方法,为线程池创建新的线程。在使用时,可以创建该接口的匿名子类对象作为参数传入。
handler:RejectedExecutionHandler是一个接口,只有一个rejectedExecution方法。ThreadPoolExecutor中有四个内部类:CallerRunsPolicy,AbortPolicy,DiscardPolicy,DiscardOldestPolicy,它们都是接口RejectedExecutionHandler的子类。
在执行新的任务时,若线程池中线程超过最大数量或者线程池关闭,会调用rejectedExecution方法,抛出RejectedExecutionException异常通知调用者。
值得一提的是,参数handler用得很少,通常可以选择其他构造方法。
三,Executors工具类
如何使用上面提到的四种线程池呢,Java的原生API提供了Executors工具类,调用对应的工厂方法可以得到线程池。
查看Executors工厂方法源码:
public class Executors {
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
}
//...
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
}
//...
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue());
}
//...
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
//...
}
第3行,FixedThreadPool,固定大小线程池。由参数可知,线程池中只有核心线程,任务队列无限大。该核心线程没有超时机制,不会被回收,于是能很快的响应执行任务的请求。
第11行,SingleThreadExecutor,单任务线程池。由参数可知,只有一个核心线程,任务队列无限大。它可以使所有的任务串行的在线程池中执行,没有多线程的同步问题。
第20行,CachedThreadPool,非固定大小线程池。由参数可知,没有核心线程,有足够多的非核心线程,非核心线程闲置时的超时时间是60s。当线程池中的非核心线程都在工作时,若需执行新的任务会创建新的线程,数量不受限制;若仍有闲置的线程,则使用空闲的线程执行新任务,但线程闲置的时间超过60s会被回收。当没有任务执行时,所有的非核心线程因超时机制的缘故,最终都会被回收,几乎不占用系统资源。
第28行,ScheduledThreadPool。继续查看ScheduledThreadPoolExecutor类的源码:
public class ScheduledThreadPoolExecutor
extends ThreadPoolExecutor
implements ScheduledExecutorService{
//...
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
//...
}
第8行,super指的是ThreadPoolExecutor类,由参数可知,核心线程数量固定,非核心线程足够多且闲置时会被立刻回收。它可以定时,间隔的循环执行任务,后面会有示例展示。
四,线程池的使用
直接上代码,xml布局:
MainActivity代码如下:
public class MainActivity extends Activity implements OnClickListener {
private Button btn_fixed_thread_pool;
private ExecutorService newFixedThreadPool;
private Runnable r1;
private Runnable r2;
private Runnable r3;
private Runnable r4;
private Runnable r5;
private ExecutorService newSingleThreadExecutor;
private Button btn_single_thread_execute;
private Button btn_cache_thread_pool;
private ExecutorService newCachedThreadPool;
private Button btn_schedule_thread_pool;
private ScheduledExecutorService newScheduledThreadPool;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
//创建5个任务
r1 = new MyRunnable();
r2 = new MyRunnable();
r3 = new MyRunnable();
r4 = new MyRunnable();
r5 = new MyRunnable();
//创建线程池
newFixedThreadPool = Executors.newFixedThreadPool(5);
newSingleThreadExecutor = Executors.newSingleThreadExecutor();
newCachedThreadPool = Executors.newCachedThreadPool();
newScheduledThreadPool = Executors.newScheduledThreadPool(3);
}
public void initView() {
btn_fixed_thread_pool = (Button) findViewById(R.id.btn_fixed_thread_pool);
btn_fixed_thread_pool.setOnClickListener(this);
btn_single_thread_execute = (Button) findViewById(R.id.btn_single_thread_execute);
btn_single_thread_execute.setOnClickListener(this);
btn_cache_thread_pool = (Button) findViewById(R.id.btn_cache_thread_pool);
btn_cache_thread_pool.setOnClickListener(this);
btn_schedule_thread_pool = (Button) findViewById(R.id.btn_schedule_thread_pool);
btn_schedule_thread_pool.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_fixed_thread_pool:
//线程池中添加5个任务
newFixedThreadPool.execute(r1);
newFixedThreadPool.execute(r2);
newFixedThreadPool.execute(r3);
newFixedThreadPool.execute(r4);
newFixedThreadPool.execute(r5);
break;
case R.id.btn_single_thread_execute:
//线程池中添加5个任务
newSingleThreadExecutor.execute(r1);
newSingleThreadExecutor.execute(r2);
newSingleThreadExecutor.execute(r3);
newSingleThreadExecutor.execute(r4);
newSingleThreadExecutor.execute(r5);
break;
case R.id.btn_cache_thread_pool:
//线程池中添加5个任务
newCachedThreadPool.execute(r1);
newCachedThreadPool.execute(r2);
newCachedThreadPool.execute(r3);
newCachedThreadPool.execute(r4);
newCachedThreadPool.execute(r5);
break;
case R.id.btn_schedule_thread_pool:
//线程池中添加任务1,并延迟3秒执行;
newScheduledThreadPool.schedule(r1, 3000, TimeUnit.MILLISECONDS);
break;
default:
break;
}
}
private class MyRunnable implements Runnable {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
Log.e("wcc", "Running thread is " + threadName);
}
}
}
代码应该比较好理解,下面给出使用四种线程池执行任务打印的log。
点击id为btn_fixed_thread_pool的按钮,使用FixedThreadPool线程池执行任务的log:
12-12 10:43:10.889: E/wcc(2312): Running thread is pool-1-thread-1
12-12 10:43:10.909: E/wcc(2312): Running thread is pool-1-thread-2
12-12 10:43:10.919: E/wcc(2312): Running thread is pool-1-thread-3
12-12 10:43:10.919: E/wcc(2312): Running thread is pool-1-thread-4
12-12 10:43:10.919: E/wcc(2312): Running thread is pool-1-thread-5
点击id为btn_single_thread_execute的按钮,使用SingleThreadExecutor线程池执行任务的log:
12-12 10:52:50.319: E/wcc(3084): Running thread is pool-2-thread-1
12-12 10:52:50.329: E/wcc(3084): Running thread is pool-2-thread-1
12-12 10:52:50.329: E/wcc(3084): Running thread is pool-2-thread-1
12-12 10:52:50.329: E/wcc(3084): Running thread is pool-2-thread-1
12-12 10:52:50.329: E/wcc(3084): Running thread is pool-2-thread-1
点击id为btn_cache_thread_pool的按钮,使用CachedThreadPool线程池执行任务的log:
12-12 11:03:17.909: E/wcc(3898): Running thread is pool-3-thread-1
12-12 11:03:17.919: E/wcc(3898): Running thread is pool-3-thread-1
12-12 11:03:17.919: E/wcc(3898): Running thread is pool-3-thread-2
12-12 11:03:17.929: E/wcc(3898): Running thread is pool-3-thread-2
12-12 11:03:17.929: E/wcc(3898): Running thread is pool-3-thread-3
点击id为btn_schedule_thread_pool的按钮,使用ScheduledThreadPool线程池执行任务的log:
12-12 11:38:12.530: E/wcc(6856): Running thread is pool-4-thread-1
(该条log是延迟3秒之后才打印出来的)
到这里,对Android中常用的四种线程池使用的介绍,到此结束~