Android线程池

来源:互联网 发布:如何禁用端口 编辑:程序博客网 时间:2024/06/05 08:40

介绍

  多线程技术主要解决处理器单元多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。

  当同时并发多个网络线程的时候,为了防止内存过度消耗和并发线程过多,控制活动线程的数量,线程池可以限制创建线程的数目,以极大的提高App的性能。

  假设在一个APP完成一项任务的时间为TT1为创建线程的时间;T2为线程中执行任务的时间,包括线程间同步所需要的时间,T3为线程销毁的时间。

  显然,T=T1+T2+T3。这是一个简化的假设,可以看出T1T3是多线程本身带来的开销,希望减少T1T3,从而减少T。但是一些线程的使用者并没有注意到这一点,仍在程序中频繁地创建或者销毁线程,这就导致了T1T3T中占用了相当大的比例。显然这就突出了线程的弱点(T1T3),而不是优点(并发性)。

线程池的优点

  1. 避免线程的创建和销毁带来的性能开销
  2. 能够很好的控制并发线程数,提高资源的利用率,避免大量的线程间因互相抢占系统资源导致的阻塞现象。
  3. 能够对线程进行简单的管理并提供定时执行间隔执行并发数量并发模式队列模式等定制功能。

线程池的基本组成

  1. 线程池管理器(ThreadPool):用于创建和管理线程池,包括创建线程池、销毁线程池、添加新任务。
  2. 工作线程(PoolWorker):线程池中的线程,在没有任务的时候处于等待的状态,可以循环执行任务。
  3. 任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口、任务执行完之后的收尾工作、任务的执行状态等等。
  4. 任务队列(TaskQueue):用于存放没有处理的任务,提供一种缓冲机制。

  线程池技术正式关注如何缩短或者调整T1T3的技术,从而提高服务器程序性能。它把T1T3分别安排在服务器程序的启动和结束的时间段和一些空闲的时间段,这样子服务器程序在处理客户请求的时候,就不会有T1T3的开销了。

  线程池不仅仅调整了T1T3产生的时间段,而且还显著的减少了创建线程的数目。

  假如一个服务器每天的要处理10W个请求,并且每个请求需要一个单独的线程完成。在线程池中,线程数一般是固定的,所以产生的线程总数不会超过线程池中线程的数目;如果服务器不利用线程池来处理这些请求,则线程总数为10W个。一般的线程池是远小于10W个的,所以利用线程池的服务器程序是不会为了创建10W个线程而在处理请求的时候浪费时间,从而提高效率。

  1. CachedThreadPool:缓存型池子,先查看池中有没有以前创建的线程,如果有就重用,如果没有就建一个新的线程加入池子中。能重用的线程必须是timeout IDLE内的池中线程,默认timeout是60s(默认是60s),超过这个IDLE时长,线程实例将被终止并移出池。缓存型池子通常用于执行一些生存期很短的异步型任务。
  2. FixedThreadPool:创建固定大小的线程池,每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小,线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。和CachedThreadPool不同的是,FixedThreadPool池线程数是固定的,但是0秒IDLE(无IDLE)。这也就意味着创建的线程会一直存在。所以FixedThreadPool多针对一些很稳定的很固定的正规并发线程,多用于服务器。
  3. ScheduledThreadPool:调度型线程池。支持定时以及周期性的任务执行,类似与Timer,0秒IDLE(无IDLE)。
  4. SingleThreadExecutor:单例线程,任意时间池中只能有一个线程,并且所有的任务是串行执行的,保证所有任务按照制定顺序先进先出(FIFO)执行。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。

代码演示

public class MainActivity extends AppCompatActivity        implements View.OnClickListener {    /**     * 依据CPU的个数创建活动线程的个数     */    private static final int COUNT = Runtime.getRuntime().availableProcessors()            * 3 + 2;    private static final String TAG = "Executor";    private static ExecutorService mCacheThreadExecutor;    private static ExecutorService mFixedThreadExecutor;    private static ScheduledExecutorService mScheduleThreadExecutor;    private static ExecutorService mSingleThreadExecutor;    private Button cached;    private Button fixed;    private Button scheduled;    private Button single;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        initView();        initListener();        initExecutorService();    }    private void initListener() {        this.cached.setOnClickListener(this);        this.fixed.setOnClickListener(this);        this.scheduled.setOnClickListener(this);        this.single.setOnClickListener(this);    }    private void initView() {        this.cached = findViewById(R.id.cache);        this.fixed = findViewById(R.id.fix);        this.scheduled = findViewById(R.id.schedule);        this.single = findViewById(R.id.single);    }    @Override    public void onClick(View v) {        switch (v.getId()) {            case R.id.cache:                ExecutorServiceThread(mCacheThreadExecutor);                break;            case R.id.fix:                ExecutorServiceThread(mFixedThreadExecutor);                break;            case R.id.schedule:                ExecutorScheduleThread(mScheduleThreadExecutor);                break;            case R.id.single:                ExecutorServiceThread(mSingleThreadExecutor);                break;            default:                break;        }    }    private void initExecutorService() {        // 一个没有限制最大线程数的线程池,        // 由60的的生存周期,过了60s就会被移出线程池。        mCacheThreadExecutor = Executors.newCachedThreadPool();        // 限制线程池大小为count的线程池        mFixedThreadExecutor = Executors.newFixedThreadPool(COUNT);        // 一个按照制定时间周期性执行的线程池        mScheduleThreadExecutor = Executors.newScheduledThreadPool(COUNT);        // 每次只执行一个线程任务的线程池        mSingleThreadExecutor = Executors.newSingleThreadExecutor();    }    private void ExecutorServiceThread(ExecutorService executorService) {        for (int i = 0;i < 9; i++) {            final int index = i;            executorService.execute(() -> {                try {                    // 暂停两秒钟                    Thread.sleep(2 * 1000);                } catch (InterruptedException e) {                    e.printStackTrace();                }                Log.i(TAG, "Thread:" + Thread.currentThread().getId() +                        ",activeCount:" + Thread.activeCount() +                        ",index:" + index);            });        }    }    private void ExecutorScheduleThread(ScheduledExecutorService executorService) {        for (int i = 0;i < 9; i++) {            final int index = i;            executorService.schedule(() -> {                try {                    // 暂停两秒钟                    Thread.sleep(2 * 1000);                } catch (InterruptedException e) {                    e.printStackTrace();                }                Log.i(TAG, "Thread:" + Thread.currentThread().getId() +                        ",activeCount:" + Thread.activeCount() +                        ",index:" + index);            }, 2, TimeUnit.SECONDS);        }    }}

CachedThreadPool

  启动CachedThreadPool:
这里写图片描述
  等待60秒:
这里写图片描述
  从图中可以看出,191~199是最先创建的线程。过了60秒,重新点击创建线程的按钮的时候,由于当前的线程池中的线程的生存周期已过且都被移出线程池,因此新建了200~208号线程。
  让我们直接在IDEA中直接演示我们写的代码的效果:
这里写图片描述
  注意:途中使用的线程监控的软件是JDK自带的,位于JDK的BIN目录下的jconsole

FixedThreadPool

这里写图片描述
  从两次中可以看出,FixedThreadPool创建的线程会一直存在。
这里写图片描述
  创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。并且创建的线程会一直的存在在线程池中。
这里写图片描述

ScheduledThreadPool

这里写图片描述
  依次按照delay的时间执行或者周期性执行。
这里写图片描述
  每隔两秒会执行一次。

SingleThreadExecutor

这里写图片描述
  SingleThreadExecutor中线程池中等待的线程会等待正在的线程执行完之后才会开始执行。
这里写图片描述

Android网络请求使用示例

public void startThread(Runnable runnable) {    ThreadFactory threadFactory = new ThreadFactoryBuilder()            .setNameFormat("mdns-pool-%d").build();    ExecutorService singleThreadPool = new ThreadPoolExecutor(            1, 1, 0L, TimeUnit.MICROSECONDS,            new LinkedBlockingDeque<>(1024),            threadFactory, new ThreadPoolExecutor.AbortPolicy()    );    singleThreadPool.execute(runnable);    singleThreadPool.shutdown();}

说明:

public ThreadPoolExecutor(int corePoolSize,                          int maximumPoolSize,                          long keepAliveTime,                          TimeUnit unit,                          BlockingQueue<Runnable> workQueue,                          ThreadFactory threadFactory) {    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,         threadFactory, defaultHandler);}
参数 说明 corePoolSize 线程池的核心线程数。一般情况下线程池中的线程在没有任务的时候会一直存在在线程池中,只有在只有在 ThreadPoolExecutor 中的方法 allowCoreThreadTimeOut(boolean value) 设置为 true 时,闲置的核心线程会存在超时机制,如果在指定时间没有新任务来时,核心线程也会被终止,而这个时间间隔由第3个属性 keepAliveTime 指定。 maximumPoolSize 线程池所能容纳的最大线程数,当活动的线程数达到这个值后,后续的新任务将会被阻塞。 keepAliveTime 控制线程闲置时的超时时长,超过则终止该线程。一般情况下用于非核心线程,只有在 ThreadPoolExecutor 中的方法 allowCoreThreadTimeOut(boolean value) 设置为 true时,也作用于核心线程。 unit 用于指定 keepAliveTime 参数的时间单位,TimeUnit 是个 enum 枚举类型,常用的有:TimeUnit.HOURS(小时)、TimeUnit.MINUTES(分钟)、TimeUnit.SECONDS(秒) 和 TimeUnit.MILLISECONDS(毫秒)等。 workQueue 线程池的任务队列,通过线程池的 execute(Runnable command) 方法会将任务 Runnable 存储在队列中。 threadFactory 线程工厂,它是一个接口,用来为线程池创建新线程的。

附录

  • Android物联网开发——基于Android Studio环境(何福贵编著)
  • 线程、多线程与线程池总结
    • 写得很清晰的一篇文章。
  • JAVA进阶—-ThreadPoolExecutor机制
原创粉丝点击