Android中的线程池(二)
来源:互联网 发布:淘宝小视频保存到手机 编辑:程序博客网 时间:2024/06/16 20:44
上一篇博客Android中的线程池(一),简单分析了线程池的内部工作的过程,有兴趣的同学可以去阅读下。那真的是简单分析,因为在那篇文章里,只从一个任务从提交到被执行的过程简单分析。事实上线程池的内部实现原理是挺复杂的。
这篇博客介绍各种线程池。等我哪一天真正完全理解了线程池的内部每一个细节的实现,会写一篇完整的源码分析文章。
线程池说白了就是用于管理线程的。他的作用总结如下:
1. 复用线程池中的线程,避免了大量的线程创建和销毁带来的性能开销
2. 有效控制线程池的最大并发线程数,避免大量的线程之间因为抢占竞争系统的资源而阻塞
3. 提供定时执行,并发数控制等功能
线程池都实现了ExecutorService接口,该接口的实现类有ThreadPoolExecutor,ScheduledThreadPoolExecutor。线程池中有线程,并且封装了任务队列。
从api开始分析。
ThreadPoolExecutor
ThreadPoolExecutor的构造方法如下:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)
corePoolSize:核心线程数
如果线程池中的线程数量,未达到核心线程数量,那么就会开启一个核心线程来执行任务
maximumPoolSize:线程池中所能容纳的最大线程数
如果任务队列已经满了,无法插入了,此时如果线程数量还没有达到线程池的最大值,那么会启动一个非核心线程来执行任务。
keepAliveTime:超时时长
非核心线程处于闲置状态如果超过了这个时长,就会被回收,当然核心线程也是可以被回收的(核心线程默认会在线程池中一直存活)。如果我们将allowCoreThreadTimeOut属性设为true的话,核心线程处于闲置状态超过了keepAliveTime设定的时长,也会被回收。可以看下allowCoreThreadTimeOut的注释说明
/** * If false (default), core threads stay alive even when idle. * If true, core threads use keepAliveTime to time out waiting * for work. */private volatile boolean allowCoreThreadTimeOut;
unit:超时时长keepAliveTime的单位
workQueue:任务队列
如果线程池中的线程数量已经达到,或者已经超过核心线程数量,那么任务会被插入到任务队列,排队等待执行。
threadFactory:线程工厂
通常不需要用到它
Handler:拒绝策略
如果线程数量已经达到线程池规定的最大值了,那么就拒绝执行此任务。
好了,详细介绍了各个参数。我们先总结一下线程池:
提交给线程池的任务,会优先被核心线程执行,如果核心线程全部都在运行状态,无闲置核心线程了,那么新的任务会进入任务队列排队等待,遵循先进先出的原则。当任务队列也已经满了,但是还有新的任务进来,那么分两种情况
1. 如果线程池还没满(还没达到最大线程数量,还能装入线程),此时会启动非核心线程来执行任务,非核心线程执行完任务,处于闲置状态后,遵循超时策略,超出时长会被回收。所以不必担心线程数量一直在积压
2. 如果线程池已经满了,装不下了,那么会有拒绝策略,拒绝新的任务进来
继续前进。
通过配置ThreadPoolExecutor的构造方法的参数,可以实现很多种线程池。我们的前辈们已经帮我们分好类了。上一篇也提过,线程池通过工厂方法来创建,我们最常用到的线程池是FixedThreadPool,那就先介绍他了。
1.FixedThreadPool
只需要简单的一行代码就能完成FixedThreadPool的创建工作
ExecutorService executorService = Executors.newFixedThreadPool(6);
我们就这样创建了容纳恒定6个线程的线程池
newFixedThreadPool方法如下:
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());}
其实就是内部调用ThreadPoolExecutor的构造方法嘛,然后配置一些参数,所以说ThreadPoolExecutor很核心。
可以看到核心线程数和最大线程数都是nThreads,超时时长为0毫秒,任务队列为无界任务队列。
可以先看看无界的任务队列LinkedBlockingQueue
public LinkedBlockingQueue() { this(Integer.MAX_VALUE);}/** * Creates a {@code LinkedBlockingQueue} with the given (fixed) capacity. * * @param capacity the capacity of this queue * @throws IllegalArgumentException if {@code capacity} is not greater * than zero */public LinkedBlockingQueue(int capacity) { if (capacity <= 0) throw new IllegalArgumentException(); this.capacity = capacity; last = head = new Node<E>(null);}
这种任务队列基于链表结构,大小是没有限制的。也就是说根本不会满
所以我们可以得出结论,FixedThreadPool中只有固定数量的核心线程,并且没有超时机制,所以即使是闲置也不会被回收,而且任务队列大小没有限制,可以一直排着长队。
2.CachedThreadPool
同样只需要一行代码就能完成CachedThreadPool的创建
ExecutorService executorService = Executors.newCachedThreadPool();
这是一个很神奇的线程池,同样看到其内部ThreadPoolExecutor的参数配置,就能了解这种线程池的功能了
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());}
他没有核心线程数的概念,也就是说他只有非核心线程。并且最大线程数是非常大的数,可以认为线程数量不定,可以任意大。幸好还有超时时长,超过了60秒的闲置线程会被回收。。不然我同时有100个任务,那就要创建100个线程了并且 还在不断的积累数量。当然这种线程如果我一个任务都没有,那么线程池中就可以一个线程都没有,全部都被回收了。为什么说这个线程池很神奇,因为他的任务队列是不能存储元素的。这样也说明了,只要有任务到达,就会立即被执行。不会放到任务队列排队。我们分析,如果任务队列不能存储新任务了,那么这种线程是适合额执行大量的耗时较少的任务。是一种用空间来换时间的做法,占用的内存大,它的并发数量越大,也就是任务越快被执行
3.SingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));}
从配置可以看出,这个线程池中只有一个线程,并且是核心线程。如果很多任务,全部放在任务队列排队,然后一直在复用这个线程执行任务
至于ThreadPoolExecutor执行就很简单了,一行代码,execute方法中传进任务即可。command是Runnable类型的参数。
executorService.execute(command);
当然也可以像上一篇博客写的那样用submit方法,submit是可以返回future对象,future可以对任务的执行结果进行获取等。这个看需求。
可以尝试提交100个任务进入线程池,每个任务休眠2秒模拟一下。这样就能感受线程池了。
ScheduledThreadPoolExecutor
从名字上看,这种线程池是可以用于执行定时任务的。我们可以这样使用:
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(6);//定时执行,1000ms后执行command任务scheduledExecutorService.schedule(command,1000, TimeUnit.MILLISECONDS);//周期重复执行,先延时10ms,然后每隔1000ms执行一次command任务scheduledExecutorService.scheduleAtFixedRate(command,10,1000,TimeUnit.MILLISECONDS);
newScheduledThreadPool方法如下:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize);}
跟进ScheduledThreadPoolExecutor
public ScheduledThreadPoolExecutor(int corePoolSize) { super(corePoolSize, Integer.MAX_VALUE, DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS, new DelayedWorkQueue());}
核心线程数固定,但是可以容纳任意多个线程。同样的有超时时长
private static final long DEFAULT_KEEPALIVE_MILLIS = 10L;
关于scheduleAtFixedRate方法,参数一是传入执行的任务,参数二是第一次运行任务时的延迟时间,参数三是定时任务的周期,参数四是时间单位
好了。以上就是Android中常用到的线程池。那么期待下一篇AsyncTask的实现原理
- Android中的线程池(二)
- 总结:Android中的线程,线程池相关(二)---线程池
- Android中的线程池和AsyncTask异步任务(二)
- android中的线程(二上)
- android中的线程(二下)
- android中的线程通信(二)
- Android中的多线程处理(二)——使用线程池中的线程
- Android中的线程机制(Handler Looper)(二)
- Android中的线程机制(Handler Looper)(二)
- android中的线程机制(二)—SocketCallBack
- C#中的线程(二)线程同步
- C#中的线程(二)线程同步
- JAVA中的线程(二)
- java37java中的线程(二)
- Android中的线程池
- Android中的线程池
- Android中的线程池
- Android 中的线程池
- python opencv 教程资料
- Android在选择系统相册图片时,返回的图片地址不同导致程序崩溃!!!
- Java验证码
- matlab 画三维图像
- MyBatis对表执行CRUD(增删改查)操作
- Android中的线程池(二)
- 回溯法
- 项目SVN分支项目和并操作
- Nginx配置文件详细说明
- 查看Oracle数据库DBA角色,以及如何去除用户的DBA权限
- CodeFirst int类型主键问题 column does not allow nulls. INSERT fails.
- iOS 读取图片的各种方法
- 最简单SSH工具:PUTTY/PSCP/PSFTP
- 为什么你应该(从现在开始就)写博客