android中的线程池学习笔记
阅读书籍: Android开发艺术探索 Android开发进阶从小工到专家
对线程池原理的简单理解:
创建多个线程并且进行管理,提交的任务会被线程池指派给其中的线程进行执行,通过线程池的统一调度和管理使得多线程的使用更简单,高效.

使用线程池的优势:
1.重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销.
2.能有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象.
3.能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能.
ThreadPoolExecutor
Android中的线程池都是直接或者间接通过配置ThreadPoolExecutor来实现的,通过不同的参数可以创建不同的线程池.
ThreadPoolExecutor类中有四个构造方法,下面是比较常用的一个.
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
corePoolSize
线程池的核心线程数,默认情况下,核心线程会在线程池中一直存活,即使他们处于闲置状态.可以通过设置ThreadPoolExecutor的allowCoreThreadTimeOut属性为true,这样闲置的核心线程在等待新任务到来时会有超时策略,这个时间间隔由keepAliveTime所指定.
maximumPoolSize
线程池所能容纳的最大线程数,当活动线程数达到这个数值后,后续的新任务将会被阻塞.
keepAliveTime
非核心线程闲置时的超时时长,闲置超过这个时长,非核心线程就会被回收.当ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true时,keepAliveTime同样会作用于核心线程.
unit
用于指定keepAliveTime参数的时间单位,这是一个枚举,例如TimeUnit.MILLISECONDS等.
workQueue
线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个参数中.
threadFactory
线程工厂,为线程池提供创建新线程的功能.ThreadFactory是一个接口,它只有一个方法: Thread newThread(Runnable r).
除了以上这些参数外,还有一个不常用的参数RejectedExecutionHandler handler.当线程池无法执行新任务时(这可能是由于任务队列已满或者是无法成功执行任务),会调用handler的rejectedExecution方法来通知调用者,默认情况下该方法会直接抛出一个RejectedExecutionException.(该参数不常用)
ThreadPoolExecutor执行任务时,大致遵循如下规则:
(1)如果线程池中的线程数量未达到核心线程数,那么会直接启动一个核心线程来执行任务.
(2)如果线程池中的线程数量已经达到或者超过核心线程的数量,那么任务会被插到任务队列中排队等待执行.
(3)如果在步骤2中无法将任务插入到任务队列中(这往往是由于任务队列已满),这个时候如果线程数量未达到线程池规定的最大值,那么会立刻启动一个非核心线程来执行任务.
(4)如果步骤3中线程数量已经达到线程池规定的最大值,那么就拒绝执行此任务,ThreadPoolExecutor会调用RejectedExecutionHandler的RejectedExecution方法来通知调用者.
线程池的分类
从线程池的功能特性上来说,Android的线程池主要分为四类,这4类线程池可以通过Executors所提供的工厂方法来得到.
FixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
特点:
从参数中可以看到FixedThreadPool中只有核心线程并且这些核心线程没有超时机制,任务队列也是没有大小限制的.
FixedThreadPool线程数量固定,当线程处于空闲状态时,它们并不会被回收,除非线程池被关闭了.当所有的线程都处于活动状态时,新任务都会处于等待状态,直到有线程空闲出来.
由于只有核心线程并且这些核心线程不会被回收,这意味着它能够更加快速地响应外界的请求.
使用场景:
对于Android平台,由于资源有限,最常使用的就是通过Executors.newFixedThreadPool(int size)来启动固定数量的线程池
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fixedThreadPool(3);
}
private static void fixedThreadPool(int size) {
ExecutorService executorService = Executors.newFixedThreadPool(size);
for (int i = 0;i < MAX; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
SystemClock.sleep(2000);
Log.i("MainActivity","执行线程: " + Thread.currentThread().getName());
}
});
}
}
执行结果:
03-31 16:03:01.016 25903-25934/com.example.janiszhang.threadpooldemo I/MainActivity: 执行线程: pool-1-thread-1
03-31 16:03:01.016 25903-25935/com.example.janiszhang.threadpooldemo I/MainActivity: 执行线程: pool-1-thread-2
03-31 16:03:01.016 25903-25936/com.example.janiszhang.threadpooldemo I/MainActivity: 执行线程: pool-1-thread-3
03-31 16:03:03.016 25903-25934/com.example.janiszhang.threadpooldemo I/MainActivity: 执行线程: pool-1-thread-1
03-31 16:03:03.016 25903-25935/com.example.janiszhang.threadpooldemo I/MainActivity: 执行线程: pool-1-thread-2
03-31 16:03:03.016 25903-25936/com.example.janiszhang.threadpooldemo I/MainActivity: 执行线程: pool-1-thread-3
03-31 16:03:05.016 25903-25935/com.example.janiszhang.threadpooldemo I/MainActivity: 执行线程: pool-1-thread-2
03-31 16:03:05.016 25903-25934/com.example.janiszhang.threadpooldemo I/MainActivity: 执行线程: pool-1-thread-1
03-31 16:03:05.016 25903-25936/com.example.janiszhang.threadpooldemo I/MainActivity: 执行线程: pool-1-thread-3
03-31 16:03:07.016 25903-25935/com.example.janiszhang.threadpooldemo I/MainActivity: 执行线程: pool-1-thread-2
从结果中可以看到,每隔2s有三个线程同时运行,而且是相同的三个线程.
CachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
特点
从参数中可以看出,它是一种线程数量不定的线程池,而且没有核心线程,只有非核心线程,并且最大线程数为Integer.MAX_VALUE(由于Integer.MAX_VALUE是一个很大的数(0x7FFFFFFF),实际上就相当于最大线程数可以任意大).有60s超时机制,当整个线程池都处于闲置状态时,线程池中的线程都会超时而被停止,这个时候几乎不占用任何系统资源.
使用场景
从CachedThreadPool的特性来看,这类线程池比较适合执行大量的耗时较少的任务.(以空间换时间)
//省略onCreate函数
private static void cachedThreadPool() {
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0;i < MAX; i++) {
executorService.execute(new Runnable() {
@Override
public void run() {
SystemClock.sleep(2000);
Log.i("MainActivity","执行线程: " + Thread.currentThread().getName());
}
});
}
}
执行结果:
03-31 16:48:47.776 6757-6791/com.example.janiszhang.threadpooldemo I/MainActivity: 执行线程: pool-1-thread-7
03-31 16:48:47.776 6757-6785/com.example.janiszhang.threadpooldemo I/MainActivity: 执行线程: pool-1-thread-1
03-31 16:48:47.776 6757-6787/com.example.janiszhang.threadpooldemo I/MainActivity: 执行线程: pool-1-thread-3
03-31 16:48:47.776 6757-6788/com.example.janiszhang.threadpooldemo I/MainActivity: 执行线程: pool-1-thread-4
03-31 16:48:47.776 6757-6789/com.example.janiszhang.threadpooldemo I/MainActivity: 执行线程: pool-1-thread-5
03-31 16:48:47.776 6757-6790/com.example.janiszhang.threadpooldemo I/MainActivity: 执行线程: pool-1-thread-6
03-31 16:48:47.776 6757-6786/com.example.janiszhang.threadpooldemo I/MainActivity: 执行线程: pool-1-thread-2
03-31 16:48:47.776 6757-6792/com.example.janiszhang.threadpooldemo I/MainActivity: 执行线程: pool-1-thread-8
03-31 16:48:47.776 6757-6793/com.example.janiszhang.threadpooldemo I/MainActivity: 执行线程: pool-1-thread-9
03-31 16:48:47.776 6757-6794/com.example.janiszhang.threadpooldemo I/MainActivity: 执行线程: pool-1-thread-10
从结果来看,每次调用execute,(在没有空闲线程可用情况下)就会立即启动一个线程,所有任务几乎在同时完成.
ScheduledThreadPool
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
特点及使用场景:
从参数列表可以看出,它的核心线程数是固定的,非核心线程数没有限制.
这类线程池主要用于执行定时任务和具有固定周期的重复任务.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(4);
//2000ms后执行command
scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
SystemClock.sleep(2000);
Log.i("MainActivity", "执行线程: " + Thread.currentThread().getName());
}
},2000, TimeUnit.MILLISECONDS);
//延迟10ms后,每隔1000ms执行一次command
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
SystemClock.sleep(2000);
Log.i("MainActivity", "执行线程: " + Thread.currentThread().getName());
}
},10,1000,TimeUnit.MILLISECONDS);
}
SingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
特点与使用场景:
这类线程池内部只有一个核心线程,它确保所有的任务都在同一个线程中按顺序执行.SingleThreadExecutor的意义在于统一所有的外界任务到一个线程中,这使得在这些任务之间不需要处理线程同步的问题.
线程池的最佳大小
线程池的最佳大小取决于可用处理器的数目以及工作队列中的任务性质.
若在一个具有N个处理器的系统上只有一个工作队列,其中全部是计算性质的任务,在线程池具有N或N+1个线程时一般会获得最大的CPU利用率.
对于那些可能需要等待I/O完成的任务,需要线程池的大小超过可用处理器的数目,因为并不是所有线程都一直在工作.
android中的线程池学习笔记的更多相关文章
- android中对线程池的理解与使用
前段时间有幸接到腾讯上海分公司的 Android开发面试,虽然最后一轮被毙了.但还是得总结一下自己在android开发中的一些盲点,最让我尴尬的是面试官问我几个android中线程池的使用与理解..哎 ...
- Android中的线程池
在Android中,主线程不能执行耗时的操作,否则可能会导致ANR.那么,耗时操作应该在其它线程中执行.线程的创建和销毁都会有性能开销,创建过多的线程也会由于互相抢占系统资源而导致阻塞的现象.这个时候 ...
- c++11 线程池学习笔记 (一) 任务队列
学习内容来自一下地址 http://www.cnblogs.com/qicosmos/p/4772486.html github https://github.com/qicosmos/cosmos ...
- Android中的线程池概述
线程池 Android里面,耗时的网络操作,都会开子线程,在程序里面直接开过多的线程会消耗过多的资源,在众多的开源框架中也总能看到线程池的踪影,所以线程池是必须要会把握的一个知识点; 线程运行机制 开 ...
- Android中的线程池 ThreadPoolExecutor
线程池的优点: 重用线程池中的线程,避免因为线程的创建和销毁带来的性能消耗 能有效的控制线程的最大并发数,避免大量的线程之间因抢占系统资源而导致的阻塞现象 能够对线程进行简单的管理,并提供定时执行以及 ...
- android开发学习 ------- 【转】 android中的线程池
线程很常见 , https://blog.csdn.net/seu_calvin/article/details/52415337 参考,保证能看懂.
- c++11 线程池学习笔记 (二) 线程池
学习内容来自以下地址 http://www.cnblogs.com/qicosmos/p/4772486.html github https://github.com/qicosmos/cosmos ...
- Android开发之线程池使用总结
线程池算是Android开发中非常常用的一个东西了,只要涉及到线程的地方,大多数情况下都会涉及到线程池.Android开发中线程池的使用和Java中线程池的使用基本一致.那么今天我想来总结一下Andr ...
- Android中后台线程如何与UI线程交互
我想关于这个话题已经有很多前辈讨论过了.今天算是一次学习总结吧. 在android的设计思想中,为了确保用户顺滑的操作体验.一些耗时的任务不能够在UI线程中运行,像访问网络就属于这类任务.因此我们必须 ...
随机推荐
- 浅谈requireJS
项目中大都使用模块化开发,requireJS作为AMD模块开发的典范,所以有必要学习下.通过一步步利用requireJS编写demo,从而学习requireJS的一个整体开发流程以及自我使用requi ...
- How to write perfect C code
Several days ago, I was involved in an argument about choice of C or C++. What I ignored was "l ...
- 如何用C#代码判断一个类的类型
var s = ""; s.GetType().IsClass; 来自为知笔记(Wiz)
- 负margin的原理以及应用
负margin在布局中往往起到意想不到的效果,比如在多栏等高布局中就是用该技巧. 虽说网络上关于负margin的实践有很多,但对margin负值为什么会出现这样的效果却没有多少讲解,本篇的目的就是阐述 ...
- Struts2环境下Tomcat启动异常:Exception starting filter struts2,报了一个java.lang.ClassNotFoundException
在写一个struts2+hibernate整合的小例子时,启动Tomcat服务器,报了一个: 严重: Exception starting filter struts2java.lang.ClassN ...
- Kooboo CMS - @Html.FrontHtml().HtmlTitle() 详解
首先我们找到这个类. 这个类有如下的方法: #region Title & meta [Obsolete("Use HtmlTitle")] public IHtmlStr ...
- 使用GIT@OSChina 实现协同工作的方法。
由于我新建了一个团队,团队里的人对于GIT都不太熟悉,所以才有了这篇文章.我用的是git-1.9.4的版本,所以我建议团队里面的成员也使用这个版本.首先是下载git,这个自己去网上找吧,一大堆,记得是 ...
- 【LeetCode】Palindrome Pairs(336)
1. Description Given a list of unique words. Find all pairs of distinct indices (i, j) in the given ...
- C# BitArray
使用C#实现Huffman对文件进行压缩和解压缩,那个对Huffman编码后的01串没找到好的方法来保存,就很愚蠢的使用字符串保存"01"串,功能实现了,但是感觉总是有些别扭.就搜 ...
- C# GZip对字符串压缩和解压
/// <summary> /// 压缩方法 /// </summary> public static string CompressString(string str) { ...