浅谈对JAVA线程池的理解(一)

来源:互联网 发布:故宫淘宝实体店地址 编辑:程序博客网 时间:2024/05/29 10:11

        在android开发中避免不了地会经常和线程打交道,我们常用的线程类不外乎Thread和AsyncTask这两个,单线程还好说,new一个Thread或者AsyncTask对象,执行一下就OK了,基本上就能满足很多简单的需求了,困难点是遇到多线程怎么办,比如要异步地下载50个图片,我们不能简单地创建50个线程,因为进程维护每一个线程都是要花费时间和空间的,线程数目多了,会占用大量的cpu资源,不仅消耗内存空间,还会增大CPU调度的负荷,造成ANR或者应用崩溃,而且这种方式创建的线程基本都上是各自为战,管理起来也十分困难。这时候使用java线程池就显得尤为必要。

        线程池的概念,顾名思义,是一个容器,这个容器盛放的是线程,有了这个容器,我们就可以避免线程的泛滥式创建,可以管理创建的线程,还可以知道线程的运行状态,是多线程编程的良器。ThreadPoolExecutor是线程池的核心类,线城池的使用就是用这个类,创建这个类的对象,然后拿着这个对象就可以利用线程池了,关键是很多初级的编程人员对怎样创建ThreadPoolExecutor对象不是很了解,使用不同的构造器创建的线程池对象具有不同的特性,所以了解ThreadPoolExecutor的构造方法及其参数的准确含义对线程池的正确使用有很大帮助。本篇总结了一下这方面的知识,以对刚刚接触线程池的读者有所帮助。

先介绍几个比较简单创建线程池的方法,利用Executors类:

1、Executors.newSingleThreadExecutor()
这个方法创建一个可以最多保留一个线程的线程池,使用该线程池时,如果里面还没有线程,则会创建一个线程,该线程会一直保持存在,及时处于空闲状态,线程池会一直维护它的存在,当要执行新的任务时,如果该线程处于空闲状态,线程池会使用该线程去完成,如果不处于空闲状态,线程池不会创建新的线程,而是把新提交的任务会保存在一个任务队列等待,直至该线程处于空闲状态。

2、Executors.newFixedThreadPool(intcoreThreadNum)
这个方法创建的线程池会维护coreThreadNum个线程,这些线程称作核心线程。当提交一个新任务时,如果线程池内有空闲线程,则会选择一个空闲线程来处理任务,如果没有空闲线程,并且线程数小于coreThreadNum,线程池会创建一个新线程来处理任务,而如果线程数等于coreThreadNum,线程池会把任务保存在一个任务队列里面,直至有空闲线程出现,而不会创建新的线程。也就是说该线程池会维持不超过coreThreadNum的线程。

3、Executors.newCachedThreadPool()
这个方法创建的线程池是比较自由的一种线程池,它对线程的数目没有限制,它不维持任何线程,当有新任务提交时,就会创建一个新线程来执行,当线程执行完毕之后就会立即被销毁,这种线程池就是来个新任务就创建一个新线程,线程数目的上限是Integer.MAX_VALUE。

4、Executors.newScheduledThreadPool(intcoreThreadNum)
这个方法创建的线程池在创建线程时综合了2、3方法的特点,会维持最多coreThreadNum个核心线程,即当提交新任务时,有空闲线程直接用空闲线程来执行任务,若线程池没有空闲线程并且线程数小于coreThreadNum时,线程池会创建新线程,该新线程会被线程池一直维护着,及时空闲状态也不会被终结。当线程池的线程数目达到coreThreadNum并有新任务提交时,若有空闲线程,则选一个空闲线程来执行,否则创建新线程,但是该线程在执行完任务后会被立即销毁。该线程池还有一个重要的特点,它可另行安排在给定的延迟后运行命令,或者定期执行命令,这个要调用不同的schedule方法。

       上面介绍的几种方式是比较快捷的创建线程池对象的方式,不需要传很多参数,接受起来比较容易,但是上面的方法归根结底都是调用ThreadPoolExecutor的构造方法,ThreadPoolExecutor的构造方法有很多参数,这些参数对线程池的特性有很大影响,下面详细解说一下ThreadPoolExecutor的构造方法(这里参考的是http://www.cnblogs.com/dolphin0520/p/3932921.html这篇博客的一些内容):

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnitunit,BlockingQueue<Runnable>workQueue);

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnitunit,BlockingQueue<Runnable>workQueue,ThreadFactorythreadFactory);

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,longkeepAliveTime,TimeUnitunit,BlockingQueue<Runnable>workQueue,

                                                  RejectedExecutionHandler handler);

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,longkeepAliveTime,TimeUnitunit,BlockingQueue<Runnable>workQueue,

                                                  ThreadFactory threadFactory,RejectedExecutionHandler handler);

通过阅读android sdk源码可知,ThreadPoolExecutor继承了AbstractExecutorService类,并提供了四个构造器,事实上,通过观察每个构造器的源码具体实现,发现前面三个构造器都是调用的第四个构造器进行的初始化工作。

corePoolSize:核心池的大小,这个参数跟后面讲述的线程池的实现原理有非常大的关系。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;

maximumPoolSize:线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程;

keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;

unit:参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:

TimeUnit.DAYS;               //天TimeUnit.HOURS;             //小时TimeUnit.MINUTES;           //分钟TimeUnit.SECONDS;           //秒TimeUnit.MILLISECONDS;      //毫秒TimeUnit.MICROSECONDS;      //微妙TimeUnit.NANOSECONDS;       //纳秒

workQueue:一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般来说,这里的阻塞队列有以下几种选择:

ArrayBlockingQueue;LinkedBlockingQueue;SynchronousQueue;

ArrayBlockingQueue和PriorityBlockingQueue使用较少,一般使用LinkedBlockingQueue和Synchronous。线程池的排队策略与BlockingQueue有关。

threadFactory:线程工厂,主要用来创建线程;

handler:表示当拒绝处理任务时的策略,有以下四种取值:

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务

好了,线程池的创建基本上就讲完了,这里总结一下,使用线程池,其实就是省去了你自己去创建线程的过程,线程池会替你完成创建线程的工作,而且由于ThreadPoolExecutor封装的其他方法,我们还可以对线程进行管理,说白了,线程池就是代替你创建、管理线程的一个工具类,根据你传入的参数不同,线程池创建线程的方式也就不同。线程池创建好了,如何如执行一个任务呢,一般是调用submit方法,也有execute方法,关于线程池的一些重要方法将在下篇介绍。

1 0
原创粉丝点击