深入理解 线程池

在Android中,因为主线程的限制,执行一些耗时操作都必须在子线程中执行,使用子线程的时候多数都直接new一个,之后把执行的结果通过handler传输给主线程。
new的线程,我们没办法进行管理,只能在执行结束后去做相应的操作,假设第一个创建的线程未执行完毕,继续new多个线程,线程之间会进行竞争,可能会因为占有过多的资源而导致oom,死机或者线程被强制干掉。毕竟大量的创建和销毁都会消耗系统资源。

使用线程池的好处
.1.避免因为重复的创建和销毁而导致系统造成的开销。
.2.线程并发的管控,避免无限制的创建线程,有效的管理线程。
.3.提高系统的响应速度,避免等待线程的创建,直接放入队列进行操作。
.4.方便处理一些需要同步的操作。
.5.可控线程数量,提供定时等功能。

一、线程池介绍
  • 创建
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor();
  • 构造方法
  public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }
  • 参数介绍
    int corePoolSize
    核心线程数:核心线程一直存活在线程池中,有新任务直接取线程进行操作。
    threadPoolExecutor.allowCoreThreadTimeOut(true);的时候会根据keepAliveTime来执行超时策略,一旦超过超时的时间,闲置的核心线程会被终止。
    int maximumPoolSize
    线程池中所容纳的最大线程数(核心数+非核心数),如果活动的线程达到这个数量之后,后续的任务会被阻塞。
    long keepAliveTime
    非核心线程的闲置超时时长,如果要控制核心线程需allowCoreThreadTimeOut为true。
    TimeUnit unit
    时间单位
    BlockingQueue<Runnable> workQueue
    阻塞队列有四种:
    ArrayBlockingQueue基于数组实现的阻塞队列,先进先出原则对队列中的元素进行排序。
    LinkedBlockingQueue基于链表实现的阻塞队列,先进先出原则对队列中的元素进行排序。
    SynchronousQueue内部没有任何容量的阻塞队列,有任务的话会新建一个线程,超时时长为60秒。
    PriorityBlockingQueue 具有优先级的无限阻塞队列。
    ThreadFactory threadFactory
    为线程池提供创建线程的操作,这是一个接口,内部就Thread newThread(Runnable r);
    RejectedExecutionHandler handler
    也是一个接口,可以理解为由ThreadPoolExecutor调用接口里的方法处理不同的情况。调用条件是当队列已满并且活动线程已经是限定的最大值或者是无法执行成功的任务才会调。默认情况为AbortPolicy,就是直接抛出异常RejectedExecutionException。
    {可选情况分类
    CallerRunsPolicy 只用调用者所在线程来运行任务。
    AbortPolicy 直接抛出RejectedExecutionException异常。
    DiscardPolicy 丢弃掉该任务,不进行处理。
    DiscardOldestPolicy 丢弃队列里最近的一个任务,并执行当前任务。
    }
  • 调用
        ThreadPoolExecutor threadPoolExecutor = 
new ThreadPoolExecutor(3,5,20,TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>());

threadPoolExecutor.execute(new Runnable() {
            @Override
            public void run() {
                Log.e("MainActivity", "execute方式");
            }
        });
线程池执行流程

1.如果线程数量没有达到核心的线程数量,启动一个核心线程来执行任务。
2.如果线程池中的线程数量已经超过核心线程数,这时候任务就会被插入到任务队列中排队等待执行。
3.如果任务队列已满,任务无法插入到任务队列中。这个时候如果线程池中的线程数量没有达到线程池所设定的最大值,那么这时候就会启动一个非核心线程来执行任务。
4.如果线程池中的数量达到了所规定的最大值,那么就会拒绝执行此任务,这时候就会调用RejectedExecutionHandler中的rejectedExecution方法来通知调用者。

二、 四种线程池类型

都是直接或间接配置ThreadPoolExecutor 实现的常见线程池。

  • newFixedThreadPool
ExecutorService service = Executors.newFixedThreadPool(3);

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

设置的参数就是所能容纳的最大线程数,设置的线程为核心线程。线程池空闲状态的时候,线程不会被回收,如果所有线程都有任务执行,新任务会处于等待状态,直到有线程空闲出来。
由于newFixedThreadPool只有核心线程,并且这些线程都不会被回收,也就是 它能够更快速的响应外界请求 。从下面的newFixedThreadPool方法的实现可以看出,newFixedThreadPool只有核心线程,并且不存在超时机制,采用LinkedBlockingQueue,所以对于任务队列的大小也是没有限制。

  • newCachedThreadPool
 ExecutorService service = Executors.newCachedThreadPool();

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

newCachedThreadPool方法在这里我们可以看出它的核心线程数为0,线程最大数量为Integer.MAX_VALUE,这个值是一个很大的数,也可以理解为任意大、
当线程池有任务的时候,就会创建一个线程来执行任务,如果任务执行完毕后,线程闲置时间超过60秒的话就会被回收掉,所以说在60秒后线程池中不存在任何线程,这个时候是不占有任何资源的。

  • newScheduledThreadPool
ScheduledExecutorService  service = Executors.newScheduledThreadPool(3);

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

它的核心线程数是固定的,对于非核心线程几乎可以说是没有限制的,并且当非核心线程处于限制状态的时候就会立即被回收。

创建一个可定时执行或周期执行任务的线程池:

ScheduledExecutorService service = Executors.newScheduledThreadPool(3);
service.schedule(new Runnable() {
    public void run() {
        System.out.println("延迟三秒操作"+Thread.currentThread().getName());
    }
}, 3, TimeUnit.SECONDS);
service.scheduleAtFixedRate(new Runnable() {
    public void run() {
        System.out.println("延迟三秒后每隔2秒操作"+Thread.currentThread().getName());
    }
}, 3, 2, TimeUnit.SECONDS);

schedule(Runnable command, long delay, TimeUnit unit):延迟一定时间后执行Runnable任务;
schedule(Callable callable, long delay, TimeUnit unit):延迟一定时间后执行Callable任务;
scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit):延迟一定时间后,以间隔period时间的频率周期性地执行任务;
scheduleWithFixedDelay(Runnable command, long initialDelay, long delay,TimeUnit unit)::与scheduleAtFixedRate()方法很类似,但是不同的是scheduleWithFixedDelay()方法的周期时间间隔是以上一个任务执行结束到下一个任务开始执行的间隔,而scheduleAtFixedRate()方法的周期时间间隔是以上一个任务开始执行到下一个任务开始执行的间隔,也就是这一些任务系列的触发时间都是可预知的。

  • newSingleThreadExecutor
ExecutorService service = Executors.newSingleThreadExecutor();

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

newSingleThreadExecutor将所有的任务统一到一个线程中执行,所以在这个任务执行之间我
们不需要处理线程同步的问题。

总结

1.使用线程池需要看需求,如果是io密集任务,可以多设置线程来提高cpu的利用率,如果是cpu密集任务就是cpu个数+1的线程。
2.在需要统一管理线程的情况下,可以使用。
3.想避免过多的开销,可以使用。
4.想规定线程执行队列,可以使用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,688评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,559评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,749评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,581评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,741评论 3 271
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,684评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,122评论 2 292
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,847评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,441评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,939评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,333评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,783评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,275评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,830评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,444评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,553评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,618评论 2 249

推荐阅读更多精彩内容

  • 为什么使用线程池 当我们在使用线程时,如果每次需要一个线程时都去创建一个线程,这样实现起来很简单,但是会有一个问题...
    闽越布衣阅读 4,231评论 10 45
  • 情诗十九首 【尽管此刻很远,但我真爱你,像个狱人洗清罪名】 【尽】 你与你比邻的人走过 我守着空寂的城 拉开帘子 ...
    陆诗明阅读 1,084评论 4 19
  • 我们四位评委共同见证了美容室助理的实操考试,相信每个人都是认真对待的,检查出的小问题希望大家以后不会再犯,都会按正...
    Tracy_zhang阅读 130评论 0 0
  • 感谢图灵社区的电子书阅读奖励计划。 本书的目标人群是「希望深入学习 JavaScript 并开发完整的 Web 应...
    ltaoo阅读 391评论 0 0
  • 学完了两期的理财营,总算是及格毕业了。在学习理财的过程中,把扭曲了十来年的购物习惯给憋过来了。今天,我们来谈谈理财...
    礼小礼er阅读 564评论 1 3