关于Android中常用的四种线程池的介绍

一,写在前面

       线程池的好处:可以在设定固定数量的线程池,并重用线程池中的线程,避免了线程不断的创建和销毁带来的性能开销。另外,还可以定时以及间隔循环执行任务。
      
       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中常用的四种线程池使用的介绍,到此结束~


    










你可能感兴趣的:(java)