Android线程池的入门

来源:互联网 发布:网络红人毒药身世 知乎 编辑:程序博客网 时间:2024/06/05 04:54

此博客是我通过观看麦子学院的视频进行总结的,视频地址:

http://www.maiziedu.com/course/918-13459/

JAVA线程池分为以下几种
- newCachedThreadPool
- newFixedThreadPool
- newSingleThreadExecutor
- newScheduledThreadPool
- 总结

首先我们先来想一下我们平常都是怎样创建的:

匿名类的创建

new Thread(new Runnable() {            @Override            public void run() {            }        }).start();

这样来看普通创建是存在弊端的(下面会结合代码说明)

  1. 每次new Thread 新创建对象的性能差
  2. 线程缺乏统一管理,可能无限制的新建线程,相互之间竞争,可能会占用过多的系统资源导致OOM
  3. 缺乏更多的功能,比如定时执行,定期执行,线程中断

线程池的好处
1. 重用存在的线程,减少对象的创建,消亡的开销,性能佳
2. 可有效控制最大并发的线程数,提高系统资源使用率,避免过多竞争,避免堵塞
3. 提供定时执行,定期执行,单线程,并发数控制等功能

下面就是怎么使用线程池

所有的案例我们都是使用for循环创建了10个任务,有的任务是耗时的有的是不耗时的

new Thread

这个就不用过多的解释了。。。。。。

代码:

for(int i=0;i<10;i++){            final int index=i;            try {                Thread.sleep(1000);            } catch (InterruptedException e) {                e.printStackTrace();            }            new Thread(new Runnable() {                @Override                public void run() {                    Log.e("TAG","new Thread:"+"\n"+"name:"+Thread.currentThread().                            getName()+"\n"+"i:"+index);                }            }).start();        }

打印日志:

这里写图片描述  这里写图片描述  这里写图片描述

可以看出每一个Thread的name 都是不一样的,这样可不就创建了很多个对象了吗,造成资源浪费

newCachThreadPoll

简介:创建一个可缓存的线程池,如果线程池长度超过处理需要,可灵活回收空线程,若无可回收,继续创建新的线程

代码:

 //所有的都是ExecutorService的一个对象        ExecutorService cacheThreadPoll= Executors.newCachedThreadPool();        //创建10个子线程的任务        for (int i=0;i<10;i++){            //i在下面取不到,赋值给index            final int index=i;            //每执行一次任务就休息两秒钟            try {                Thread.sleep(1000);            } catch (InterruptedException e) {                e.printStackTrace();            }            cacheThreadPoll.execute(new Runnable() {                @Override                public void run() {                    Log.e("TAG","cacheThreadPoll"+"\n"+"name:"+Thread.currentThread().                            getName()+"\n"+"i:"+index);                }            });        }

打印日志:

这里写图片描述  这里写图片描述  这里写图片描述

这些线程的名字都是一样的,也就是说只创建了一个对象,就避免了资源的浪费

那么当第一个任务还没执行完,没有空闲的线程对象可以使用了会怎么办呢,我们就可以模拟一下(把休眠去掉即可)  
这里写图片描述  这里写图片描述  这里写图片描述

可以看出来这个线程池中创建了5个线程对象,有的线程对象被使用了多次,并且i的打印顺序不对,那为什么我们停止休眠操作后会出现这个问题呢

i的执行顺序:

CPU在处理子线程任务的时候是随机的,也是穿插执行的,我们同时并发了10个子线程0123…9,因为没有睡眠,所以这些子线程几乎是同时执行,它在执行的时候会随机执行,并且可能会在1上执行一点然后又去2执行,2还没执行完又回到1执行了,所以就造成了i的打印不是顺序的

线程池开启了5个子线程:

当前1号线程被占用,所以只能再开启2号线程去执行其他任务了,当1号线程执行完它的任务之后,它会看看哪些任务还是没有执行的,再去执行其他的任务,最后2号执行完任务并发现没有别的任务了,2号就被回收了

newFixedThreadPool

简介:创建一个定长线程池,可控制线程最大的并发数,超出线程会在队列中等待
代码:

//定义线程池的大小为3        ExecutorService fixedThreadPool=Executors.newFixedThreadPool(3);        for (int i=0;i<10;i++){            final int index=i;            fixedThreadPool.execute(new Runnable() {                @Override                public void run() {                    Log.e("TAG","fixedThreadPool:"+"\n"+"name:"+Thread.currentThread().                            getName()+"\n"+"i:"+index);                }            });        }

打印日志:
这里写图片描述  这里写图片描述  这里写图片描述

和上面不同,这里不管有几个任务并发,只会有3个线程对象去执行任务,其他的任务只能等着了~

newSingleThreadExecutor

简介:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO,LIFO)优先执行

代码:

//创建一个单一线程池        ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();        for (int i = 0; i < 10; i++) {            final int index = i;            singleThreadPool.execute(new Runnable() {                @Override                public void run() {                    Log.e("TAG", "singleThreadPool:" + "\n" + "name:" + Thread.currentThread().                            getName() + "\n" + "i:" + index);                }            });        }

打印日志:

这里写图片描述  这里写图片描述  这里写图片描述

不管我们的任务有多少,我们的线程对象只会开启一个

newScheduledThreadPool

简介:创建一个定长的线程池,支持定时及周期性任务的执行,比如两秒之后想做什么,或者是每两秒想做什么

定时执行:

 //得到当前毫秒数        Log.e("TAG", "textScheduled start:"+System.currentTimeMillis());        //创建一个定长的线程池,并且里面的线程对象都是单一的        ScheduledExecutorService scheduledExecutorService = Executors.                newSingleThreadScheduledExecutor();        //定义3秒之后执行什么        scheduledExecutorService.schedule(new Runnable() {            @Override            public void run() {                //三秒钟之后会执行到打印Log这一步                Log.e("TAG", "delay 3 seconds:"+System.currentTimeMillis());            }

打印日志:

这里写图片描述

中间是相差三秒钟的,并且这个线程只执行了一次

周期性的执行:

 //周期性执行任务,2秒后执行,每隔3秒执行一次        scheduledExecutorService.scheduleAtFixedRate(new Runnable() {            //控制执行次数的变量            @Override            public void run() {                Log.e("TAG", "delay 2 seconds,and execute every 3seconds");            }        }, 2, 3, TimeUnit.SECONDS);

打印日志:

这里写图片描述

从textScheduled start 到下面的delay…..的带引中间相差了2s,然后每个delay……之间的打印中间相差了3s而且是一直打印下去

停止周期循环:

scheduledExecutorService.shutdown();

总结

不难看出他们之间的使用是有一些规律的

  1. 创建对象——— Executors.newXXXXExecutor( ) ;创建出一个 ExecutorService 的对象,当然 ScheduledExecutorService 是 newScheduledThreadPool 的对象
  2. 将任务放置到线程池———xxxx.execute,参数传递的是一个Runnable接口(newScheduledThreadPool 例外 )