android 线程池

先来看一看ThreadPoolExecutor的一个常用的构造方法。

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        ...
    }

看这构造器,一眼看上去,哇(wo)塞(cao)。这个构造器算少的了,其实每个参数非常好理解,这里简单说一下吧。

  • int corePoolSize表示核心线程数,在不设置allowCoreThreadTimeOut为ture的情况下,核心线程就算没事做也不会被销毁。
  • int maximumPoolSize最大线程数。
  • long keepAliveTime超时时长,一个非核心线程(设置allowCoreThreadTimeOut为ture也同样作用于核心线程)在处于闲置状态(没事做)超过这个时长就会被销毁。
  • TimeUnit unit时间单位,有秒、毫秒、微秒...等。
  • BlockingQueue<Runnable> workQueue缓存任务队列。

这里就不过多的介绍参数的意义,需要的可以自行去查查,这里着重来看看常用的4个线程池的基本用法。

可缓存线程池 CachedThreadPool

先看下源码

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

根据源码可以看出:

  1. 这种线程池内部没有核心线程,线程的数量是有没限制的。
  2. 在创建任务时,若有空闲的线程时则复用空闲的线程,若没有则新建线程。
  3. 没有工作的线程(闲置状态)在超过了60S还不做事,就会销毁。
    我会的专业术语就会这点了(口水咽下,请别喷....),下面直接看例子吧。
buju.png

这是整个布局界面,简直low的不行就不多说了,一眼就看裸了。

mCachedThreadPool = Executors.newCachedThreadPool();//创建可缓存线程池
//开始下载
private void startDownload(final ProgressBar progressBar, final int i) {
        mCachedThreadPool.execute(new Runnable() {
            @Override
            public void run() {
                int p = 0;
                progressBar.setMax(10);//每个下载任务10秒
                while (p < 10) {
                    p++;
                    progressBar.setProgress(p);
                    Bundle bundle = new Bundle();
                    Message message = new Message();
                    bundle.putInt("p", p);
                    //把当前线程的名字用handler让textview显示出来
                    bundle.putString("ThreadName", Thread.currentThread().getName());
                    message.what = i;
                    message.setData(bundle);
                    mHandler.sendMessage(message);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }

上面这段代码.....没有什么难度和技术。我们先下载A,在A还没有下载完成的时候再下载B,可以看到是这样的。

1.png

看textview后面显示的Thread名字,“thread-1”和“thread-2”很明显不是同一个线程。当A下载完成之后(60S)内,然后下载C(图中B也还没有下载完成)。

2.png

看到下载C任务的线程时“thread-1”。复用了之前空闲的线程,省去了创建线程的时间。接下来当ABC都下载好了60S以后(当然你可以自己创建一个线程池把60S改小些)。再去下载D和E。

3.png

现在就是线程“thread-3”“thread-4”创建了新的线程,说明之前的线程因为超过了设置的时间不干活被炒了。

FixedThreadPool 定长线程池

老规矩先帖下源码

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

根据源码

  1. 该线程池的最大线程数等于核心线程数,所以在默认情况下,该线程池的线程不会因为闲置状态超时而被销毁。
  2. 如果当前线程数小于核心线程数,并且也有闲置线程的时候提交了任务,这时也不会去复用之前的闲置线程,会创建新的线程去执行任务。如果当前执行任务数大于了核心线程数,大于的部分就会进入队列等待。等着有闲置的线程来执行这个任务。
    看下例子,例子大部分还是上面的例子,只是修改了一下,首先创建线程池
mFixedThreadPool = Executors.newFixedThreadPool(3);//创建定长线程池

这个设置了3个核心线程数。然后把上面startDownload方法修改一下

  private void startDownload(final ProgressBar progressBar, final int i) {
        mFixedThreadPool.execute(new Runnable() {
            @Override
            public void run() {
               //....逻辑代码和上面一样
            }
        });
    }

现在先下载A,等到A下载完成再下载B,会看到下面结果

1.png

可以看到现在即使是“thread-1”处于闲置状态,再开始B任务,也不会复用“thread-1”。会创建一个新的“thread-2”新的线程来执行任务。
接下来我们下载C,这时又会创建一个新的线程“thread-3”来执行任务。现在我们设置的3个线程数已经有了,接下来再下载D。

2.png

这个时候是“thread-1”来执行的任务。也就是说当我们现在的核心线程数达到我们设置的线程数之后,再来执行任务,如果线程池中有闲置的线程,就会复用之前闲置的线程来执行任务。如果现在要执行的任务超过了现在可用的线程,那么超过的任务就会进入队列等待。现在我们重启一下程序,直接点击全部下载。

3.gif

点击全部下载之后,由于我们只设置了3个线程数,所以DE就加入了等待队列,等到前边下载完成空闲了之后才开始执行DE任务。

SingleThreadPool

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

这个线程池很明显了吧,就只有一个线程,所有的任务都遵循入队出队规则。这个线程池就不演示了。

ScheduledThreadPool

老规矩,98号...啊呸...上源码

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

DEFAULT_KEEPALIVE_MILLIS就是默认10L,这里就是10秒。这个线程池有点像是吧CachedThreadPool和FixedThreadPool 结合了一下。

  1. 不仅设置了核心线程数,最大线程数也是Integer.MAX_VALUE。
  2. 这个线程池是上述4个中为唯一个有延迟执行和周期执行任务的线程池。

首先创建线程池

 mScheduledThreadPool = Executors.newScheduledThreadPool(3);

一般的执行任务方法和上面的都大同小异,我们主要看看延时执行任务和周期执行任务的方法。

mScheduledThreadPool.schedule(new Runnable() {
            @Override
            public void run() {
            //....
            }
        }, 3, TimeUnit.SECONDS);

这个方法一看就知道,表示在3秒之后开始执行我们的任务。

mScheduledThreadPool.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
             //....
            }
        },3, 7, TimeUnit.SECONDS);
mScheduledThreadPool.scheduleWithFixedDelay(new Runnable() {
            @Override
            public void run() {
             //....
            }
        },3, 7, TimeUnit.SECONDS);

咦?上面两个一毛一样?注(yan)意(xia)看(a)。两个方法的方法名都不一样,参数是相同的。意思上也都是延迟3秒之后开始执行任务,每隔7秒执行一次。这尼玛一样啊,玩珠珠啊。别急,这里我就直接给出两个方法的不同,有兴趣的可以去自己试试。

  • 第一个方法是延迟3秒后执行任务,从开始执行任务这个时候开始计时,每7秒执行一次不管执行任务需要多长的时间。
  • 第二个方法是延迟3秒后执行任务,从任务完成时这个时候开始计时,7秒后再执行,再等完成后计时7秒再执行也就是说这里的循环执行任务的时间点是从上一个任务完成的时候。

常用的4个线程池的基本用法都说的差不多了,在各位老司机面前耍关公了。第一次.....难免有些紧张,表现不好请各位客官见谅。

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

推荐阅读更多精彩内容