Android中线程池值得掌握一波

该文的起源呢,是要归结到上一篇的AsyncTask源码整理一波,也是为了更好地掌握线程池这一块的知识。下面咱们看一个原始用线程的事例:

public class ExcuterActivity extends AppCompatActivity {
    private static final String TAG = ExcuterActivity.class.getSimpleName();
    ProgressBar progressBar;
    ProgressBar progressBar1;
    ProgressBar progressBar2;
    ProgressBar progressBar3;

    private Handler handler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            int what = msg.what;
            if (what == 0) {
                progressBar1.setProgress((int) msg.obj);
            } else if (what == 1) {
                progressBar2.setProgress((int) msg.obj);
            } else if (what == 2) {
                progressBar3.setProgress((int) msg.obj);
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_excuter);
        progressBar1 = findViewById(R.id.progress1);
        progressBar2 = findViewById(R.id.progress2);
        progressBar3 = findViewById(R.id.progress3);
        for (int i = 0; i < 3; i++) {
            final int progress = 0;
            final int what = i;
            new Thread() {
                @Override
                public void run() {
                    super.run();
                    excute(progress, what);
                }

            }.start();
        }

    }

    private void excute(int progress, int what) {
        while (progress < 100) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                progress++;
                Message message = handler.obtainMessage(what);
                message.obj = progress;
                handler.sendMessage(message);
            }
        }
    }
}

相信大家很熟悉上面的代码了吧,开启了三个线程,然后每过1秒增加一个进度,让handler处理消息。我想问如果任务很多的时候呢,是不是得创建很多个线程,所以这种做法肯定是不行的,下面就会衍生出线程池来了。其实线程池就是用来管理线程的,专门用一个队列来管理剩余的任务。android中用到的几种线程池其实主要围绕ThreadPoolExecutor来派生出来的,所以下面主要来说明该类:

image.png

这张图包括了ThreadPoolExecutor所有的构造器,那咱们直接去看下参数最多的构造器:
image.png

corePoolSize:核心线程的个数
maximumPoolSize:线程池中最大的线程个数(最大线程个数=核心线程+非核心线程)
keepAliveTime:非核心线程在空闲的时候等待的时间
unit:上面参数等待的时间单位
workQueue:线程队列,能设置该队列能承载的最多线程
threadFactory:线程工厂,用于设置线程的名字,可以不用关心该参数
handler:当任务超过了队列能容载的任务时,处理的策略
先来看一个基本的例子:

private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);

        public Thread newThread(Runnable r) {
            return new Thread(r, "thread #" + mCount.getAndIncrement());
        }
    };
private final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 5, 1,
            TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>(10), sThreadFactory);

在上面初始化了3个核心的线程,然后最大的线程是5个,非核心线程在空闲的时候等待的时间是1秒,任务队列最大允许有10个任务,定义了一个sThreadFactory只是为了打印线程的名字。

image.png

执行的地方换成了threadPoolExecutor来执行。
image.png

excute获取每个线程的进度,以及每个线程的名字。

咋们再看下打印的日志:

image.png

可以看出来每过1秒钟,同时3个线程的progress加1。
如果线程池中的线程数未达到核心线程的个数,则会立马开启一个新的核心线程去执行

下面试着增大任务的个数为5个,其他的配置不变,看看情况会咋样:


image.png

可以看出来,这里用到的还是核心线程,并且将前面的1和2两个线程放到了线程队列中,等到1前面的3个任务执行完了后,让队列中的线程去处理后面的任务。

如果线程池的个数大于核心线程的个数,并且线程队列还能装下线程,因此让核心线程排到线程队列中,等到非队列的线程任务执行完了后,才会执行队列中的线程。
那下面把线程的个数填满线程队列,上面设置的线程队列最大容载是10个线程,10个+核心线程3个=13个。那咱们设置14个看下会发生什么,为了看到效果我把progress的界限改了下:

image.png

image.png

再执行看下:

image.png

可以看得出来,核心线程总共是3个,线程队列是10个线程容量,而任务是14个,因此当线程队列满了10个的时候,还需要一个线程,因此创建了一个非核心线程4来执行任务。
为了验证这个猜测,我们现在再增加任务到16个,看是不是创建了3个非核心线程,这里我把最多的线程个数调到7个,方便我们观察线程的动态:
image.png

image.png

可以看出来确实创建了3个非核心线程,也验证了我们的结论:
当线程队列满的时候,如果还有任务需要执行,此时需要几个线程就需要创建几个非核心线程
上面都是未超过非核心线程的个数,那么如果线程队列也满了,而且需要剩下的线程个数超过了非线程的个数会咋样呢,这里我把任务继续调到18个,那此时需要的非核心线程是不是就是18-(3个核心线程+10个线程队列的个数)=5个非核心线程,而我们定义的非核心线程是4个,那此时看看会发生什么:
image.png

这里抛了一个异常信息:
image.png

意思是线程的总共个数是7个,不能达到需要线程的个数。因此这里可以得出结论:
在任务需要非核心线程个数大于设置的最大非核心线程的个数时候,此时是直接抛RejectedExecutionException异常。
说完了上面的几种情况,其实java里面给提供了几种常用的线程池,在Executors类中有如下几种线程池:
FixedThreadPool

image.png

image.png

image.png

从日志也看得出来,总共是3个线程在倒腾,这个没什么好说的。
这里可以看到最大线程数和核心线程数是相等的,说明没有非核心线程的说法了,也就是自始至终都只有核心线程。
SingleThreadExecutor
image.png

image.png

image.png

看到日志大家也明白了,自始至终只有一个线程在工作。

只有一个核心线程,和我们平常new一个thread是一个道理
CachedThreadPool

image.png

该线程只有非核心线程,并且非核心线程在空闲的时候等60s就销毁了
其他的几种线程池就自己看了,这里只是列举出一两种。

总结

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

推荐阅读更多精彩内容