Android - 线程池

线程池优点,四种线程池介绍

http://blog.csdn.net/u012702547/article/details/52259529

 

为什么要有线程池:

1.使用new Thread()创建线程存在的问题

1.针对每一个item都创建一个新线程,这样会导致频繁的创建线程,线程执行完之后又被回收,又会导致频繁的GC

2.这么多线程缺乏统一管理,各线程之间互相竞争,降低程序的运行效率,手机页面卡顿,甚至会导致程序崩溃

3.如果一个item滑出页面,则要停止该item上图片的加载,但是如果使用这种方式来创建线程,则无法实现线程停止执行

 

如果使用线程池,我们就可以很好的解决以上三个问题。

2.使用线程池的好处

1.重用已经创建的好的线程,避免频繁创建进而导致的频繁GC

2.控制线程并发数,合理使用系统资源,提高应用性能

3.可以有效的控制线程的执行,比如定时执行,取消执行等

 

线程池的主要参数:

这里是7个参数(我们在开发中用的更多的是5个参数的构造方法),OK,那我们来看看这里七个参数的含义:

 

 

corePoolSize  线程池中核心线程的数量

maximumPoolSize  线程池中最大线程数量

keepAliveTime 非核心线程的超时时长,当系统中非核心线程闲置时间超过keepAliveTime之后,则会被回收。如果ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,则该参数也表示核心线程的超时时长

unit 第三个参数的单位,有纳秒、微秒、毫秒、秒、分、时、天等

workQueue 线程池中的任务队列,该队列主要用来存储已经被提交但是尚未执行的任务。存储在这里的任务是由ThreadPoolExecutor的execute方法提交来的。

threadFactory  为线程池提供创建新线程的功能,这个我们一般使用默认即可

handler 拒绝策略,当线程无法执行新任务时(一般是由于线程池中的线程数量已经达到最大数或者线程池关闭导致的),默认情况下,当线程池无法处理新线程时,会抛出一个RejectedExecutionException。

 

针对于workQueue参数我多说几点:workQueue是一个BlockingQueue类型,那么这个BlockingQueue又是什么呢?它是一个特殊的队列,当我们从BlockingQueue中取数据时,如果BlockingQueue是空的,则取数据的操作会进入到阻塞状态,当BlockingQueue中有了新数据时,这个取数据的操作又会被重新唤醒。同理,如果BlockingQueue中的数据已经满了,往BlockingQueue中存数据的操作又会进入阻塞状态,直到BlockingQueue中又有新的空间,存数据的操作又会被冲洗唤醒。BlockingQueue有多种不同的实现类,下面我举几个例子来说一下:

1.ArrayBlockingQueue:这个表示一个规定了大小的BlockingQueue,ArrayBlockingQueue的构造函数接受一个int类型的数据,该数据表示BlockingQueue的大小,存储在ArrayBlockingQueue中的元素按照FIFO(先进先出)的方式来进行存取。

2.LinkedBlockingQueue:这个表示一个大小不确定的BlockingQueue,在LinkedBlockingQueue的构造方法中可以传一个int类型的数据,这样创建出来的LinkedBlockingQueue是有大小的,也可以不传,不传的话,LinkedBlockingQueue的大小就为Integer.MAX_VALUE

 

CachedThreadPool

CachedTreadPool一个最大的优势是它可以根据程序的运行情况自动来调整线程池中的线程数量,CachedThreadPool源码如下:

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

我们看到,CachedThreadPool中是没有核心线程的,但是它的最大线程数却为Integer.MAX_VALUE,另外,它是有线程超时机制的,超时时间为60秒,这里它使用了SynchronousQueue作为线程队列,SynchronousQueue的特点上文已经说过了,这里不再赘述。那么我们提交到CachedThreadPool消息队列中的任务在执行的过程中有什么特点呢?由于最大线程数为无限大,所以每当我们添加一个新任务进来的时候,如果线程池中有空闲的线程,则由该空闲的线程执行新任务,如果没有空闲线程,则创建新线程来执行任务。根据CachedThreadPool的特点,我们可以在有大量任务请求的时候使用CachedThreadPool,因为当CachedThreadPool中没有新任务的时候,它里边所有的线程都会因为超时而被终止。

 

demo里任务如下:

callable1 = new Callable<Integer>() {
            int num = 0;

            @Override
            public Integer call() throws Exception {
                while (num < 100) {
                    num++;
                    sendMsg(num, THREAD1_START);
                    Thread.sleep(100);
                }
                return 100;
            }
        };

执行结果

同时开始三个任务:三个线程同时开始时,同时进行,进度一致。

 

SingleThreadExecutor

singleThreadExecutor和FixedThreadPool很像,不同的就是SingleThreadExecutor的核心线程数只有1,如下:

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

使用SingleThreadExecutor的一个最大好处就是可以避免我们去处理线程同步问题,其实如果我们把FixedThreadPool的参数传个1,效果不就和SingleThreadExecutor一致了

执行结果:

同时开始三个任务: 因为只有一个核心进程,所以所有的任务都添加到任务队列,按顺序执行

 

FixedThreadPool

FixedThreadPool是一个核心线程数量固定的线程池,创建方式如下:

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

我们看到核心线程数和最大线程数一样,说明在FixedThreadPool中没有非核心线程,所有的线程都是核心线程,且线程的超时时间为0,说明核心线程即使在没有任务可执行的时候也不会被销毁(这样可让FixedThreadPool更快速的响应请求),最后的线程队列是一个LinkedBlockingQueue,但是LinkedBlockingQueue却没有参数,这说明线程队列的大小为Integer.MAX_VALUE(2的31次方减1),OK,看完参数,我们大概也就知道了FixedThreadPool的工作特点了,当所有的核心线程都在执行任务的时候,新的任务只能进入线程队列中进行等待,直到有线程被空闲出来

 

我这里创建的是核心线程为2的线程池

pool = Executors.newFixedThreadPool(2);

执行结果

同时开始三个任务: 因为只有两个核心线程,前两个task同时执行,最后再执行最后一个

 

ScheduledThreadPool

ScheduledThreadPool是一个具有定时定期执行任务功能的线程池,源码如下:

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

我们可以看到,它的核心线程数量是固定的(我们在构造的时候传入的),但是非核心线程是无穷大,当非核心线程闲置时,则会被立即回收。

 

线程池其他常用功能

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

2.shutDownNow() 关闭线程池,并尝试去终止正在执行的线程

3.allowCoreThreadTimeOut(boolean value) 允许核心线程闲置超时时被回收

4.submit 一般情况下我们使用execute来提交任务,但是有时候可能也会用到submit,使用submit的好处是submit有返回值

使用submit时我们可以通过实现Callable接口来实现异步任务。在call方法中执行异步任务,返回值即为该任务的返回值。Future是返回结果,返回它的isDone属性表示异步任务执行成功!

 

posted @ 2017-07-24 00:52  qlky  阅读(217)  评论(0编辑  收藏  举报