Android | 线程池的使用和简单原理

参考文献:

1 简介

 除了①线程池,使用线程还有三种方式,分别是 ②继承Thread类 ③实现Runnable接口④实现Callable接口,这三种方式最后都需要新建和销毁线程。在实际的高并发场景下,往往线程数多,每个线程执行任务耗时短。上面三种新建线程的方式,新建和销毁线程的时间消耗经常会比实际执行任务的耗时还要长,导致提交的任务不能立即响应执行,并且新建和销毁线程往往有一定的资源消耗;其次创建新线程的方式线程缺乏统一管理,容易出现线程阻塞的情况。所以这里我们引入了一种复用线程执行任务的方式-线程池,减少新建线程的次数。
 首先说说“任务”的概念,实现Runnable或Callable接口创建的对象只能当做一个可以在线程中运行的任务,不是真正意义上的线程,所以最后还需要通过 Thread 来调用。可以说任务是通过线程驱动从而执行的。

2 原理

2.1 内部逻辑结构

线程池逻辑结构图

2.2 核心参数

线程池核心参数

ThreadPoolExecutor类是线程池中最重要的类,可以通过在该类实例化时配置不同的参数传入构造器,来实现自定义线程池;

        // 创建线程池对象,通过 构造方法 配置核心参数
   Executor executor = new ThreadPoolExecutor( CORE_POOL_SIZE,MAXIMUM_POOL_SIZE,KEEP_ALIVE,
                                                TimeUnit.SECONDS, sPoolWorkQueue,sThreadFactory );

        // 构造函数
    public ThreadPoolExecutor (int corePoolSize,int maximumPoolSize,long keepAliveTime,
                               TimeUnit unit,BlockingQueue<Runnable workQueue>,
                               ThreadFactory threadFactory )

2.3 任务执行逻辑

线程池任务执行流程图

 补充说明:

  • 处理任务优先级:核心线程 > 任务队列 > 非核心线程 > 拒绝策略handler;
  • 非核心线程空闲时间超过存活期keepAliveTime,就会被回收;一般情况,核心线程即使闲置也不会被回收;(除非当allowCoreThreadTimeOut(true)时,keepAliveTime也适用于核心线程);
  • 一般情况,线程池刚创建时,其中是没有线程的,只有新任务到来,才会创建线程执行该任务;

​ 对于线程池、核心线程、非核心线程自己的通俗理解:

​  线程池看成一个公司,核心线程可以看成是看成是公司的正式员工,非核心线程可以看成是实习生;无论是正式员工还是实习生在同一时刻只能执行一个任务。当有新任务时,老板首先找正式员工干活;如果正式员工都在干活时继续来新任务,那么多余任务就放到一个任务队列里,等有正式员工闲下来后接着干新任务;如果任务较多,超出正式员工能处理的数量,这时老板就考虑招几个实习生来干活,实习生处理任务队列满了之后新来的任务。如果任务太多了,那么老板就不再接单了,拒绝处理新来的任务。

​  一般情况,正式员工有任务到来就干活,没任务就空闲,不会被裁;实习生有任务到来就干活,没任务就空闲,空闲的时间太长了,老板不养闲人,就把这个实习生裁了。

3 基本使用

     // 1.创建线程池,通过配置核心参数,从而实现自定义线程池
   Executor threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE,MAXIMUM_POOL_SIZE,KEEP_ALIVE,
                                  TimeUnit.SECONDS,sPoolWorkQueue,sThreadFactory);
    // 注:在Java中,已内置4种常用线程池,下面会详细说明

        // 2.向线程池提交任务:execute(),传入Runnable对象
    threadPool.execute(new Runnable() {
            @Override
            public void run() {
                ... // 线程执行任务
            }
        });

    // 3. 关闭线程池shutdown() 
  threadPool.shutdown();

shutdown()shutdownNow()区别:

  • shutdown():等待正在执行任务的线程执行完再关闭线程池;
  • shutdownNow():不等待立即关闭;

4 四类常用线程池

 根据参数的不同配置,Java内置了4种常用线程池,他们的参数已经配置好了:

  • 定长线程池(FixedThreadPool)
  • 定时线程池(ScheduledThreadPool )
  • 缓存线程池(CachedThreadPool)
  • 单例线程池(SingleThreadExecutor)

4.1 定长线程池FixedThreadPool

  • 特点:只有核心线程 & 不会被回收、线程数固定、任务队列无大小限制;

  • 应用场景:控制线程池最大并发数;

//创建 定长线程池 对象 & 设置线程池线程数固定为3
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
//向线程池提交任务:execute()
fixedThreadPool.execute(() -> System.out.println("定长线程池->执行任务啦"));
//关闭线程池
fixedThreadPool.shutdown();

4.2 定时线程池ScheduledThreadPool

  • 特点:核心线程数固定、非核心线程数无限制(闲时立刻回收);

  • 应用场景:执行定时 / 周期性 任务

//创建 定时线程池 对象 & 设置线程池核心线程数固定为5
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
//创建好Runnable类线程对象 & 需执行的任务
Runnable task1 = () -> System.out.println("定时线程池->执行任务啦");
// 向线程池提交任务:schedule()
scheduledThreadPool.schedule(task1, 1, TimeUnit.SECONDS); // 延迟1s后执行任务
scheduledThreadPool.scheduleAtFixedRate(task1,10,1000,TimeUnit.MILLISECONDS);// 延迟10ms后、每隔1000ms执行任务
//关闭线程池
scheduledThreadPool.shutdown();

4.3 缓存线程池CachedThreadPool

  • 特点:只有非核心线程、线程数无限制;灵活回收空闲线程(具备超时机制,全部回收时几乎不占系统资源);无线程可用时新建线程;
  • 应用场景:执行数量多、耗时少任务
// 创建 可缓存线程池 对象
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
//向线程池提交任务1:execute()
cachedThreadPool.execute(() -> System.out.println("可缓存线程池-执行任务1"));
//向线程池提交任务2:execute();复用线程
cachedThreadPool.execute(() -> System.out.println("可缓存线程池-执行任务2"));
//关闭线程池
cachedThreadPool.shutdown();

4.4 单例线程池SingleThreadExecutor

  • 特点:只有一个核心线程,保证所有任务按顺序在一个线程中执行,不存在线程安全问题;

  • 应用场景:适合单线程任务;不适合必须使用多线程的耗时任务,如数据库操作、文件操作等;

//创建 单例线程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
//向线程池提交任务:execute()
singleThreadExecutor.execute(() -> System.out.println("单例线程池-执行任务啦"));
//关闭线程池
singleThreadExecutor.shutdown();

4.5 对比

四类常见线程池对比

5 总结

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

推荐阅读更多精彩内容

  • 第一部分 来看一下线程池的框架图,如下: 1、Executor任务提交接口与Executors工具类 Execut...
    压抑的内心阅读 4,257评论 1 24
  • 概述 JAVA通过多线程的方式实现并发,为了方便线程池的管理,JAVA采用线程池的方式对线线程的整个生命周期进行管...
    五月花号区块链联盟阅读 1,673评论 0 1
  • 世事艰辛,从不肯称我心。于别人轻而易举,于我艰难万分。求而不得,然后我假装自己不求。 我这一生从未好运,我安慰自己...
    九徒阅读 158评论 0 0
  • 人生。还有多少事情想去完成,却一直拖着没去完成的;有多少想去尝试,却一直没有鼓起勇气去尝试的;有n多少想去原谅,却...
    forever_girl阅读 246评论 0 0
  • 韩剧看起来不用脑,颜值高可以研究里面衣服打扮,细致情感说话很贴合日常演员表情到位,让人无聊不想思考的时候就可以跟剧...
    紫馨雅馨阅读 482评论 0 0