zoukankan      html  css  js  c++  java
  • java/android线程池详解

    一,简述线程池:

    线程池是如何工作的:一系列任务出现后,根据自己的线程池安排任务进行。

    如图:

     

    线程池的好处:

    1. 重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销。
    2. 能有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象。
    3. 能对线程进行简单的管理。并提供定时执行以及指定间隔循环执行等功能。

    线程池的具体实现为ThreadPoolExeutor,其接口为Executor

    ThreadPoolExecutor提供了一系列的参数用于配置线程池。

       public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
                                  long keepAliveTime,
                                  TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue,
                                  ThreadFactory threadFactory) {
            this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
                 threadFactory, defaultHandler);
        }

    参数含义如下:

    1. corePoolSize :线程池核心线程数,默认情况下,核心线程会在线程池中一直存活,即使他们处于闲置状态。其有一个allowCoreThreadTimeOut属性如果设置为true,那么核心线程池会有超时策略。超时的时长为第三个参数  keepAliveTime 。如果超时,核心线程会被终结。
    2. maxmumPoolSize: 线程池所能容忍的最大线程数,当活动线程数达到这个数值后,后续的新任务会被阻塞。
    3. keepAliveTime:非核心线程闲置时的超时时长,超过这个时长就会被非核心线程会被回收。这个参数如同第一个参数,如果设置相关属性后也会作用于核心线程。、
    4. unit:指定keepAliveTime的参数时间单位。这是一个枚举,常用的有MILLISECONDS(毫秒)、SECONDS(秒)等
    5. workQueue:线程池的任务队列,通过execute()方法(执行方法)提交的Runable对象会存储在这个参数中。
    6. threadFactory:线程工厂,为线程池提供创建新线程的功能。

    其执行任务时大致遵顼如下规则:

    1. 如果线程达到核心线程数量,那么回直接启动一个核心线程。
    2. 线程未达到核心线程的数量,任务会被插入到任务队列(workQueue)排队。
    3. 如果任务队列已满导致步骤二无法插入到任务队列,那么开启一个非核心线程执行。
    4. 如果步骤三的线程数量达到线程池规定数目(maxmumPoolSize),那么拒绝执行此任务。

    二,线程池的相关类结构解析

    看一下线程池的类结构图:

      

      说以下这几点:

         这是一个抽象工厂模式!

      Executor和Executors的区别:

      Executor仅仅是一个接口,其只有一个方法,execute()方法(执行方法)。

    public interface Executor {
    
    
        void execute(Runnable command);
    }

       而Executors是一个没有继承任何类的方法,其作为一个工厂方法类,用于创建形形色色的线程池等对象。如:FixedThreadPool、defaultThreadFactory(下面会具体讲),而这些具体的线程池方案(FixedThreadPool)亦或者自定义的线程池方案(ThreadPoolExeutor)都是来自ExecutorService接口,这个接口也是继承于Executor接口。通过类结构图可以很清楚的看到。

    如,Executors生产出ExecutorService对象,源码如下

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

     说到工厂模式,ThreadPoolExeutor的参数threadFactory是一个接口,仅有newThread方法,代码如下

      

    public interface ThreadFactory {
    
        Thread newThread(Runnable r);
    }

    其作用和executor接口类似,体现面向接口编程思想。其中例如如下两个方法,是工厂方法类Executors所生成的ThreadFactory对象。

     public static ThreadFactory defaultThreadFactory() {
            return new DefaultThreadFactory();
        }
    
           public static ThreadFactory privilegedThreadFactory() {
            return new PrivilegedThreadFactory();
        }
     

     defaultThreadFactory是这样创建线程的,如果要自定义不妨参照一下。

      

    static class DefaultThreadFactory implements ThreadFactory {
            private static final AtomicInteger poolNumber = new AtomicInteger(1);
            private final ThreadGroup group;
            private final AtomicInteger threadNumber = new AtomicInteger(1);
            private final String namePrefix;
    
            DefaultThreadFactory() {
                SecurityManager s = System.getSecurityManager();
                group = (s != null) ? s.getThreadGroup() :
                                      Thread.currentThread().getThreadGroup();
                namePrefix = "pool-" +
                              poolNumber.getAndIncrement() +
                             "-thread-";
            }
    
            public Thread newThread(Runnable r) {
                Thread t = new Thread(group, r,
                                      namePrefix + threadNumber.getAndIncrement(),
                                      0);
                if (t.isDaemon())
                    t.setDaemon(false);
                if (t.getPriority() != Thread.NORM_PRIORITY)
                    t.setPriority(Thread.NORM_PRIORITY);
                return t;
            }
        }

    三 具体线程池的使用


    说了那么多,要具体看看系统提供的线程池,主要是这四个:

    1. FixedTreadPool. fixed有稳固的含义,通过工厂方法类Executors的newFixedThreadPool方法创建。它是线程数量固定的线程池,仅有核心线程且没有超时策略,所以线程不会被回收。这意味着它能够快速的响应外界请求。

      例子:这里设置了4个runnable和2个核心线程。

      Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    Log.d("hello","sleep前");
                    SystemClock.sleep(3000);
                    Log.d("hello", "sleep后");
                }
            };
    ......
    
       ExecutorService executorService = Executors.newFixedThreadPool(2);
            executorService.execute(runnable);
            executorService.execute(runable1);
            executorService.execute(runable2);
            executorService.execute(runable3);

    输出如下,

    06-24 15:49:47.962 12462-12497/? D/hello: sleep1前
    06-24 15:49:47.962 12462-12496/? D/hello: sleep前
    06-24 15:49:50.962 12462-12497/com.example.hang.myapplication D/hello: sleep1后
    06-24 15:49:50.962 12462-12497/com.example.hang.myapplication D/hello: sleep2前
    06-24 15:49:50.972 12462-12496/com.example.hang.myapplication D/hello: sleep后
    06-24 15:49:50.972 12462-12496/com.example.hang.myapplication D/hello: sleep3前
    06-24 15:49:53.962 12462-12497/com.example.hang.myapplication D/hello: sleep2后
    06-24 15:49:53.972 12462-12496/com.example.hang.myapplication D/hello: sleep3后

    可以看到,每次仅有两个线程去执行,当其中一个执行完毕后才轮到下一个。
    当 核心线程数量改为 4 时,则会一次性执行完这4个runnable。

    1. CachedThreadPool.它是一种线程数量不定的线程池,只有非核心线程,可以简单理解为最大线程数是无限大的。CachedThreadPool的任务队列相当于一个空集合,这导致任何任务都会被立即执行,比较适合做一些大量的耗时较少的任务。

     例子,修改为将上述的  ExecutorService executorService = Executors.newFixedThreadPool(2);替换为

    ExecutorService executorService = Executors.newCachedThreadPool();,输出如下,一次性执行4个线程,且线程顺序不定。
    06-24 15:53:08.582 16801-16873/com.example.hang.myapplication D/hello: sleep2前
    06-24 15:53:08.582 16801-16872/com.example.hang.myapplication D/hello: sleep1前
    06-24 15:53:08.582 16801-16871/com.example.hang.myapplication D/hello: sleep前
    06-24 15:53:08.582 16801-16875/com.example.hang.myapplication D/hello: sleep3前
    06-24 15:53:11.582 16801-16873/com.example.hang.myapplication D/hello: sleep2后
    06-24 15:53:11.582 16801-16872/com.example.hang.myapplication D/hello: sleep1后
    06-24 15:53:11.582 16801-16871/com.example.hang.myapplication D/hello: sleep后
    06-24 15:53:11.582 16801-16875/com.example.hang.myapplication D/hello: sleep3后
    1. ScheduledThreadPool.Scheduled有 “预定的” 意思。核心线程池的数量书固定的且非核心线程池是没有限制的,非核心线程池被闲置时会被立即回收。主要用于执行定时任务和具有固定周期的重复任务。

      例子:这里是延迟2000ms后开始执行

     ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
            scheduledExecutorService.schedule(runnable,2000, TimeUnit.MILLISECONDS);
            scheduledExecutorService.schedule(runable1, 2000, TimeUnit.MILLISECONDS);
            scheduledExecutorService.schedule(runable2, 2000, TimeUnit.MILLISECONDS);
            scheduledExecutorService.schedule(runable3, 2000, TimeUnit.MILLISECONDS);

      例子:延迟10ms后开始每1000ms执行一次runnable。

     scheduledExecutorService.scheduleAtFixedRate(runnable,10,1000,TimeUnit.MILLISECONDS);
    1. SingleThreadExecutor.只有一个核心线程,所以确保所有的任务都是在一个线程里顺序执行。把所有的任务都放到一个线程,这样有一个好处是不需要处理线程同步问题。

     这里就不做例子了,就是4个排着队依次执行,且不会乱序。

      

    06-24 16:05:02.782 32057-32125/com.example.hang.myapplication D/hello: sleep前
    06-24 16:05:05.782 32057-32125/com.example.hang.myapplication D/hello: sleep后
    06-24 16:05:05.782 32057-32125/com.example.hang.myapplication D/hello: sleep1前
    06-24 16:05:08.782 32057-32125/com.example.hang.myapplication D/hello: sleep1后
    06-24 16:05:08.782 32057-32125/com.example.hang.myapplication D/hello: sleep2前
    06-24 16:05:11.782 32057-32125/com.example.hang.myapplication D/hello: sleep2后
    06-24 16:05:11.782 32057-32125/com.example.hang.myapplication D/hello: sleep3前
    06-24 16:05:14.782 32057-32125/com.example.hang.myapplication D/hello: sleep3后


     

  • 相关阅读:
    汇编与反汇编
    在Mac环境下跑汇编
    使用python-openCV对摄像头捕捉的镜头进行二值化并打上文字
    关于js中的setTimeout和setInterval
    在MacOX下安装python-opencv
    为什么在保护模式下IA-32处理器最高可访问4GB的内存
    Mac上常用的一些命令
    说说chrome上的JS调试
    Steganography-图片隐写术
    Makefile-filter和filter-out
  • 原文地址:https://www.cnblogs.com/yuhanghzsd/p/5611562.html
Copyright © 2011-2022 走看看