美文网首页
Android线程池

Android线程池

作者: Amy_LuLu__ | 来源:发表于2018-02-11 20:08 被阅读0次

    注意:本篇文章是本人阅读相关文章所写下的总结,方便以后查阅,所有内容非原创,侵权删。

    本篇文章内容来自于:
    Android开发——Android中常见的4种线程池(保证你能看懂并理解)
    Android开发之线程池使用总结
    Android开发艺术探索 任玉刚

    目录

    1. 关于线程池
    2. Executor提供的预定义的线程池
      --2.1 FixThreadPool 固定大小的线程池 线程不会回收,FixThreadPool会更快地响应外界请求(一堆人排队上公厕)
      --2.2 SingleThreadExecutor单个线程的线程池(公厕里只有一个坑位)
      --2.3 CachedThreadPool 可变大小的线程池 适合执行大量的耗时较少的任务(一堆人去一家很大的咖啡馆喝咖啡)
      --2.4 ScheduleThreadPool (4个里面唯一一个有延迟执行和周期重复执行的线程池)
    3. ThreadPoolExecutor自定义线程池
    4. 线程池实际应用

    1. 关于线程池

    线程池是任务队列和工作线程的集合,这两者组合起来实现生产者消费者模式。

    2. Executor提供的预定义的线程池

    2.1 FixThreadPool 固定大小的线程池(一堆人排队上公厕)

    //Executors类中
        public static ExecutorService newFixedThreadPool(int nThreads){
            //其中n是线程池中线程的个数
            return new ThreadPoolExecutor(nThreads, nThreads,
                                          0L, TimeUnit.MILLISECONDS,
                                          new LinkedBlockingQueue<Runnable>());
        }
    
        public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
            return new ThreadPoolExecutor(nThreads, nThreads,
                                          0L, TimeUnit.MILLISECONDS,
                                          new LinkedBlockingQueue<Runnable>(),
                                          threadFactory);
        }
    
    • 从配置参数来看,FixThreadPool只有核心线程,并且数量固定的,也不会被回收,所有线程都活动时,因为队列没有限制大小,新任务会等待执行。
    • 联想:FixThreadPool其实就像一堆人排队上公厕一样,可以无数多人排队,但是厕所位置就那么多,而且没人上时,厕所也不会被拆迁。
    • 由于线程不会回收,FixThreadPool会更快地响应外界请求,这也很容易理解,就好像有人突然想上厕所,公厕不是现用现建的。

    使用

    //使用  
    Executors.newFixThreadPool(5).execute(r); 
    

    2.2 SingleThreadExecutor单个线程的线程池(公厕里只有一个坑位)

        public static ExecutorService newSingleThreadExecutor() {
            return new FinalizableDelegatedExecutorService
                (new ThreadPoolExecutor(1, 1,
                                        0L, TimeUnit.MILLISECONDS,
                                        new LinkedBlockingQueue<Runnable>()));
        }
    
    
        public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
            return new FinalizableDelegatedExecutorService
                (new ThreadPoolExecutor(1, 1,
                                        0L, TimeUnit.MILLISECONDS,
                                        new LinkedBlockingQueue<Runnable>(),
                                        threadFactory));
        }
    
    • 从配置参数可以看出,SingleThreadPool只有一个核心线程,确保所有任务都在同一线程中按顺序完成。因此不需要处理线程同步的问题。
    • 联想:可以把SingleThreadPool简单的理解为FixThreadPool的参数被手动设置为1的情况,即Executors.newFixThreadPool(1).execute(r)。所以SingleThreadPool可以理解为公厕里只有一个坑位,先来先上。为什么只有一个坑位呢,因为这个公厕是收费的,收费的大爷上年纪了,只能管理一个坑位,多了就管不过来了(线程同步问题)。

    使用

    //使用  
    Executors.newSingleThreadPool ().execute(r);  
    

    2.3 CachedThreadPool 可变大小的线程池(一堆人去一家很大的咖啡馆喝咖啡)

        public static ExecutorService newCachedThreadPool() {
            return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                          60L, TimeUnit.SECONDS,
                                          new SynchronousQueue<Runnable>());
        }
    
        public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
            return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                          60L, TimeUnit.SECONDS,
                                          new SynchronousQueue<Runnable>(),
                                          threadFactory);
        }
    
    • CachedThreadPool只有非核心线程,最大线程数非常大,所有线程都活动时,会为新任务创建新线程,否则利用空闲线程(60s空闲时间,过了就会被回收,所以线程池中有0个线程的可能)处理任务。
    • 任务队列SynchronousQueue相当于一个空集合,导致任何任务都会被立即执行。
    • 联想:CachedThreadPool就像是一堆人去一个很大的咖啡馆喝咖啡,里面服务员也很多,随时去,随时都可以喝到咖啡。但是为了响应国家的“光盘行动”,一个人喝剩下的咖啡会被保留60秒,供新来的客人使用,哈哈哈哈哈,好恶心啊。如果你运气好,没有剩下的咖啡,你会得到一杯新咖啡。但是以前客人剩下的咖啡超过60秒,就变质了,会被服务员回收掉。
    • 比较适合执行大量的耗时较少的任务。喝咖啡人挺多的,喝的时间也不长。
      使用
    //使用  
    Executors.newCachedThreadPool().execute(r);  
    

    2.4 ScheduleThreadPool (4个里面唯一一个有延迟执行和周期重复执行的线程池)

        public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
            return new ScheduledThreadPoolExecutor(corePoolSize);
        }
    
        public static ScheduledExecutorService newScheduledThreadPool(
                int corePoolSize, ThreadFactory threadFactory) {
            return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
        }
    
        public ScheduledThreadPoolExecutor(int corePoolSize) {
            super(corePoolSize, Integer.MAX_VALUE,
                  DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
                  new DelayedWorkQueue());
        }
    
        public ScheduledThreadPoolExecutor(int corePoolSize,
                                           ThreadFactory threadFactory) {
            super(corePoolSize, Integer.MAX_VALUE,
                  DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
                  new DelayedWorkQueue(), threadFactory);
        }
    
    • 核心线程数固定,非核心线程(闲着没活干会被立即回收)数没有限制。
    • 从上面代码也可以看出,ScheduledThreadPool主要用于执行定时任务以及有固定周期的重复任务。
      使用
    //使用,延迟1秒执行,每隔2秒执行一次Runnable r  
    Executors. newScheduledThreadPool (5).scheduleAtFixedRate(r, 1000, 2000, TimeUnit.MILLISECONDS); 
    

    更多使用看Android开发之线程池使用总结的ScheduledThreadPool篇

    3. ThreadPoolExecutor自定义线程池

    Executor作为一个接口,它的具体实现就是ThreadPoolExecutor。
    Android中的线程池都是直接或间接通过配置ThreadPoolExecutor来实现不同特性的线程池。

    ThreadPoolExecutor自定义线程池

    public ThreadPoolExecutor(  
    //核心线程数,除非allowCoreThreadTimeOut被设置为true,否则它闲着也不会死  
    int corePoolSize,   
    //最大线程数,活动线程数量超过它,后续任务就会排队                     
    int maximumPoolSize,   
    //超时时长,作用于非核心线程(allowCoreThreadTimeOut被设置为true时也会同时作用于核心线程),闲置超时便被回收             
    long keepAliveTime,                            
    //枚举类型,设置keepAliveTime的单位,有TimeUnit.MILLISECONDS(ms)、TimeUnit. SECONDS(s)等  
    TimeUnit unit,  
    //缓冲任务队列,线程池的execute方法会将Runnable对象存储起来  
    BlockingQueue<Runnable> workQueue,  
    //线程工厂接口,只有一个new Thread(Runnable r)方法,可为线程池创建新线程  
    ThreadFactory threadFactory)  
    

    ThreadPoolExecutor执行任务时的心路历程是什么样的呢?
    (1)当currentSize<corePoolSize时,没什么好说的,直接启动一个核心线程并执行任务。
    (2)当currentSize>=corePoolSize、并且workQueue未满时,添加进来的任务会被安排到workQueue中等待执行。
    (3)当workQueue已满,但是currentSize<maximumPoolSize时,会立即开启一个非核心线程来执行任务。
    (4)当currentSize>=corePoolSize、workQueue已满、并且currentSize>maximumPoolSize时,调用handler默认抛出RejectExecutionExpection异常。

    关于BlockingQueue<Runnable> 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
    3.PriorityBlockingQueue:这个队列和LinkedBlockingQueue类似,不同的是PriorityBlockingQueue中的元素不是按照FIFO来排序的,而是按照元素的Comparator来决定存取顺序的(这个功能也反映了存入PriorityBlockingQueue中的数据必须实现了Comparator接口)。
    4.SynchronousQueue:这个是同步Queue,属于线程安全的BlockingQueue的一种,在SynchronousQueue中,生产者线程的插入操作必须要等待消费者线程的移除操作,Synchronous内部没有数据缓存空间,因此我们无法对SynchronousQueue进行读取或者遍历其中的数据,元素只有在你试图取走的时候才有可能存在。我们可以理解为生产者和消费者互相等待,等到对方之后然后再一起离开。

    使用

    @Override  
        protected void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);  
            setContentView(R.layout.activity_main);  
            poolExecutor = new ThreadPoolExecutor(3, 5,  
                    1, TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(128));  
    }  
      
        public void btnClick(View view) {  
            for (int i = 0; i < 30; i++) {  
                final int finalI = i;  
                Runnable runnable = new Runnable() {  
                    @Override  
                    public void run() {  
                        SystemClock.sleep(2000);  
                        Log.d("google_lenve_fb", "run: " + finalI);  
                    }  
                };  
                poolExecutor.execute(runnable);  
            }  
        } 
    

    4. 应用

    Android研究院之应用开发线程池的经典使用(二十九)
    Android下基于线程池的网络访问基础框架

    相关文章

      网友评论

          本文标题:Android线程池

          本文链接:https://www.haomeiwen.com/subject/cdkltftx.html