android线程及线程池
众所周知,在UI系统中进行一些耗时操作,都会导致卡顿现象,因为一次刷新在16ms,如果当次操作过了这个时间,那么用户就能感觉到明显的卡顿,甚至引起ANR 。
对于这种情况,一般都是再起一个线程,进行一些耗时的操作,通过继承Thread 或者实现Runnable接口,重写run方法,来实现创建新线程,进行耗时操作的需求.由于java的单继承关系,首推实现Runnable方法,这样不会太过臃肿。
private void runThread() {
Thread t1 = new Thread(new MyRunnable(this), "郭富城");
t1.start();
//强调一点,调用run方法是真的调用方法,而调用start方法则会创建新线程去调用这个run方法。
}
//实现runnable方法 至于继承Thread方法的代码 ,就不写了
static class MyRunnable implements Runnable {
private WeakReference<MainActivity> reference;
MyRunnable(MainActivity activity) {
reference = new WeakReference<MainActivity>(activity);
}
@Override
public void run() {
if (reference != null) {
MainActivity activity = reference.get();
if (activity != null) {
//子线程不能弹toast的 所以加了点东西
Looper.prepare();
Toast.makeText(activity, Thread.currentThread().getName() + " is Runing", Toast.LENGTH_SHORT).show();
Looper.loop();
}
}
}
}
另外,jdk1.5版本以后,就新加了个Callable接口,相较于Runnable接口,他可以定义返回值,并且可以抛出异常,需要实现的方法也从run方法变成了call方法。还有一点很有意思,他返回一个Future对象,通过这个对象,我们可以知道任务的进行程序,并且可以关闭这个任务。Thread是不支持这个接口的 ,所以如果要用Callable,就需要用上FutureTask.
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
简单的说,FutureTask是一种可以取消的异步任务,异步执行任务, 可以开始、取消以及查看任务是否完成, 如果任务没有执行完,get方法会导致线程阻塞, 一旦一个执行任务已经完成就不能再次开始和结束(除非执行时通过runAndReset()方法.另外,FutureTask也同样支持Runnable接口。
private void runFutureTask() {
//创建线程池 不用线程池 直接用future也可以的
final ExecutorService exec = Executors.newFixedThreadPool(5);
//创建futureTask任务
final FutureTask<String> ft = new FutureTask<String>(new MyCallable());
// 执行也可以 提交也可以 还有种invokeAll方法
exec.execute(ft);
// exec.submit(ft);
new Thread(new Runnable() {
@Override
public void run() {
try {
//get方法会自动阻塞当前线程 直到拿到结果
//所以不要放在主线程去get 不然就卡住了 虽然我试过卡个15秒也没事
final String text = ft.get();
Looper.prepare();
Toast.makeText(FutureTaskActivity.this, text, Toast.LENGTH_SHORT).show();
Looper.loop();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}).start();
//用完线程池 记得关闭掉
exec.shutdown();
}
static class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(3000);
return "代码让我快乐,代码让我秃顶";
}
}
线程是CPU调度的最小单元,对于线程的创建,销毁,调度,其实也是在耗费一定资源的,特别是某些情况下,你需要创建一堆线程进行一些简单的耗时操作时,这个资源的消耗量还是非常大的,所以就有线程池的需求了。
我们来总结一下优点吧。
1.重用线程池中的线程,避免频繁地创建和销毁线程带来的性能消耗;
2.有效控制线程的最大并发数量,防止线程过大导致抢占资源造成系统阻塞;
3.可以对线程进行一定地管理。
使用线程池 相对于每次都new 线程,性能会好很多,并且可以统一管理,功能上也能有所改善,如果需要定时执行,定期执行,线程中断等。
减少内存开销。
ThreadPoolExecutor:
ExecutorService是最初的线程池接口,ThreadPoolExecutor类是对线程池的具体实现,它通过构造方法来配置线程池的参数,我们来分析一下它常用的构造函数吧。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
参数解释:
corePoolSize,线程池中核心线程的数量,默认情况下,即使核心线程没有任务在执行它也存在的,我们固定一定数量的核心线程且它一直存活这样就避免了一般情况下CPU创建和销毁线程带来的开销。我们如果将ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,那么闲置的核心线程就会有超时策略,这个时间由keepAliveTime来设定,即keepAliveTime时间内如果核心线程没有回应则该线程就会被终止。allowCoreThreadTimeOut默认为false,核心线程没有超时时间。
maximumPoolSize,线程池中的最大线程数,当任务数量超过最大线程数时其它任务可能就会被阻塞。最大线程数=核心线程+非核心线程。非核心线程只有当核心线程不够用且线程池有空余时才会被创建,执行完任务后非核心线程会被销毁。
keepAliveTime,非核心线程的超时时长,当执行时间超过这个时间时,非核心线程就会被回收。当allowCoreThreadTimeOut设置为true时,此属性也作用在核心线程上。
unit,枚举时间单位,TimeUnit。
workQueue,线程池中的任务队列,我们提交给线程池的runnable会被存储在这个对象上。
线程池的分配遵循这样的规则:
当线程池中的核心线程数量未达到最大线程数时,启动一个核心线程去执行任务;
如果线程池中的核心线程数量达到最大线程数时,那么任务会被插入到任务队列中排队等待执行;
如果在上一步骤中任务队列已满但是线程池中线程数量未达到限定线程总数,那么启动一个非核心线程来处理任务;
如果上一步骤中线程数量达到了限定线程总量,那么线程池则拒绝执行该任务,且ThreadPoolExecutor会调用RejectedtionHandler的rejectedExecution方法来通知调用者。
四种线程池 都可以通过Executors提供:
- newCachedThreadPool:创建一个可缓存线程池,他是一个不限容量的线程池,其中创建的所有的线程都是非核心线程,如果线程池长度超过处理需要,超时时间设置为60s,如果当前没有任务,会回收空闲线程,若无可回收,则新建线程。用于执行一些生存期很短的异步型任务
public static ExecutorService new CachedThreadPool(){
return new ThreadPoolExecutor(
0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>()
);
}
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
- newFixedThreadPool :创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。池线程数固定,创建的线程会一直存在。它没有超时机制并且等待队列无限常,所以fixedThreadPool多数针对一些很稳定很固定的正规并发线程,多用于服务器。
public static ExecutorService newFixedThreadPool(int nThreads){
return new ThreadPoolExecutor(
nThreads, nThreads, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()
);
}
//创建线程池
ExecutorService mExecutor = Executors.newFixedThreadPool(5);//参数即核心线程数
- newScheduledThreadPool 创建一个定长线程池,其实他将上面两种线程池的优点全部集结成一块,他有一定数量的核心线程,并且无限容量的非核心线程,区别在于非核心线程的超时时间设定为0秒,即一旦空闲,立马回收,支持定时及周期性任务执行。
public static ScheduledThreadPool newScheduledThreadPool(int corePoolSize){
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize){
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(10);//核心线程数
- newSingleThreadExecutor 创建一个单线程化的线程池,他只有一个核心线程,它也只会用唯一的工作线程来执行任务,可以让调用者忽略线程同步的问题。
public static ExecutorService newSingleThreadExecutor(){
return new FinalizableDelegatedExecutorService(
new ThreadPoolExecutor(
1, 1, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
线程池一般用法
shutDown(),关闭线程池,需要执行完已提交的任务;
shutDownNow(),关闭线程池,并尝试结束已提交的任务;
allowCoreThreadTimeOut(boolen),允许核心线程闲置超时回收;
execute(),提交任务无返回值;
submit(),提交任务有返回值;
newCachedThreadPool缓存线程池,如果线程超出则灵活回收,如果不够则创建
newFixedThreadPool 定长线程池,可以设置最大线程数,超出的任务在队列中等候。
newScheduledThreadPool 也是定长线程池 ,支持定时及周期性任务。
newSingleThreadExecutor 单线程化的线程池,任务根据加入顺序在队列中慢慢等待执行。
除了以上这些,在Android中充当线程的角色还有AsyncTask、HandlerThread、IntentService。它们本质上都是由Handler+Thread来构成的。
AsyncTask,它封装了线程池和Handler,主要为我们在子线程中更新UI提供便利。
HandlerThread,它是个具有消息队列的线程,可以方便我们在子线程中处理不同的事务。
IntentService,我们可以将它看做为HandlerThread的升级版,它是服务,优先级更高。
参考
http://blog.csdn.net/weixin_36244867/article/details/72832632
http://blog.csdn.net/linchunquan/article/details/22382487
http://www.hchstudio.cn/2017/04/01/FutureTask%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90/
http://www.cnblogs.com/wenjiang/archive/2012/09/02/2668089.html
作者:草丛伦
链接:http://www.jianshu.com/p/d79dab197d5a
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
android线程及线程池的更多相关文章
- android中的线程池学习笔记
阅读书籍: Android开发艺术探索 Android开发进阶从小工到专家 对线程池原理的简单理解: 创建多个线程并且进行管理,提交的任务会被线程池指派给其中的线程进行执行,通过线程池的统一调度和管理 ...
- Android开发之线程池使用总结
线程池算是Android开发中非常常用的一个东西了,只要涉及到线程的地方,大多数情况下都会涉及到线程池.Android开发中线程池的使用和Java中线程池的使用基本一致.那么今天我想来总结一下Andr ...
- android线程与线程池-----线程池(二)《android开发艺术与探索》
android 中的线程池 线程池的优点: 1 重用线程池中的线程,避免了线程的创建和销毁带来的性能开销 2 能有效的控制最大并发数,避免大量线程之间因为喜欢抢资源而导致阻塞 3 能够对线程进行简单的 ...
- 《Android开发艺术探索》读书笔记 (11) 第11章 Android的线程和线程池
第11章 Android的线程和线程池 11.1 主线程和子线程 (1)在Java中默认情况下一个进程只有一个线程,也就是主线程,其他线程都是子线程,也叫工作线程.Android中的主线程主要处理和界 ...
- Android的线程和线程池
---恢复内容开始--- 一.Android线程的形态 (一)AsyncTask解析 AysncTask简介:①.实现上封装了Thread和Handler ②.不适合进行特别耗时的后台任务 Ays ...
- 《Android开发艺术探索》第11章 Android的线程和线程池
第11章 Android的线程和线程池 11.1 主线程和子线程 (1)在Java中默认情况下一个进程只有一个线程,也就是主线程,其他线程都是子线程,也叫工作线程.Android中的主线程主要处理和界 ...
- Android中线程和线程池
我们知道线程是CPU调度的最小单位.在Android中主线程是不能够做耗时操作的,子线程是不能够更新UI的.在Android中,除了Thread外,扮演线程的角色有很多,如AsyncTask,Inte ...
- Android中的线程池概述
线程池 Android里面,耗时的网络操作,都会开子线程,在程序里面直接开过多的线程会消耗过多的资源,在众多的开源框架中也总能看到线程池的踪影,所以线程池是必须要会把握的一个知识点; 线程运行机制 开 ...
- Android线程与线程池
引言 在Android中,几乎完全采用了Java中的线程机制.线程是最小的调度单位,在很多情况下为了使APP更加流程地运行,我们不可能将很多事情都放在主线程上执行,这样会造成严重卡顿(ANR),那么这 ...
随机推荐
- KMP算法_模板_C++
这个博客讲得非常优秀,可惜它是Java版本的 http://blog.csdn.net/yutianzuijin/article/details/11954939/ a 为匹配串,b 为目标串 通俗讲 ...
- python3 yield表达式形式应用
我们已知: 生成器函数:函数体内包含有yield关键字,该函数执行的结果是生成器 yield的功能: 1.与return类似,都可以返回值,但不一样的地方在于yield返回多次值,而return只能返 ...
- python学习笔记 async and await
用asyncio提供的@asyncio.coroutine可以把一个generator标记为coroutine类型,然后在coroutine内部用yield from调用另一个coroutine实现异 ...
- linux服务与进程
linux服务与进程 http://www.cnblogs.com/jamesbd/p/3567654.html linux服务与进程 1.应用程序 2.服务脚本 3.配置文件 4.查看进程 5.查看 ...
- Selenium2+python自动化65-js定位几种方法总结【转载】
前言 本篇总结了几种js常用的定位元素方法,并用js点击按钮,对input输入框输入文本 一.以下总结了5种js定位的方法 除了id是定位到的是单个element元素对象,其它的都是elements返 ...
- Nginx-Primary script unknown的报错的解决方法
配置nginx时一直报错:file not found 错误日志: [error] 12691#0: *6 FastCGI sent in stderr: "Primary script u ...
- [BZOJ2151] 种树 贪心
2151: 种树 Time Limit: 10 Sec Memory Limit: 259 MBSubmit: 1151 Solved: 613[Submit][Status][Discuss] ...
- [libgdx游戏开发教程]使用Libgdx进行游戏开发(3)-给游戏添加一些控制功能
每个游戏中都有一些只有程序员自己才知道的控制功能,比如增加金钱,满血复活,无视防御,不死等等. 都是为了方便自己调试而在测试阶段使用的功能. 正如上一章提到的:我们也需要加些只有程序员才知道的控制功能 ...
- PHP开发经常遇到的几个错误
错误1:foreach循环后留下悬挂指针 在foreach循环中,如果我们需要更改迭代的元素或是为了提高效率,运用引用是一个好办法: $arr = array(1,2,3,4); foreach($a ...
- SyntaxError: Non-ASCII character '\xe7' in file 错误的解决方法
在代码开头写下面的定义即可 #encoding:utf-8