Android线程池详解
来源:互联网 发布:上海每日房屋成交数据 编辑:程序博客网 时间:2024/06/07 15:15
android中经常会用到多线程,因为安卓中如果5S中没有响应就会出现ANR,因此耗时的任务必须放在子线程中使用。这就涉及到UI线程(main线程)和子线程的交互。其二者之间的交互涉及到Handler,Looper,message。
每次创建线程都有系统资源的开销,不可能无限的创建线程,线程过多最终会带来系统的反应速度下降,因此就必须控制线程数量,控制线程的最好办法就是设计线程池,让线程可以重复利用。
下面将会通过三种方式来创建安卓的线程池。
1、通过静态列表缓存10个子线程,创建一个主线程,每个线程中创建唯一Handler,放在子线程的代码。
2、自定义线程池中还是设计一个主线程(UI线程),同时缓存10个子线程,当然这里创建的子线程采用android提供的HandlerThread来代替上面自定义的线程。采用HandlerThread会更加简便,后续会通过代码进行说明。
3、通过系统提供的ThreadPoolExecutor来设计线程池。
一、自定义Thread来创建线程池。
public class ThreadExtractors { public class WorkThead extends Thread { private boolean mIsQuiting = false; private boolean mIsLoopEnd = false; Handler myHandler = null; private final String mThreadId; public WorkThead(String threadId){ mThreadId = threadId; } @Override public void run() { //Looper.prepare() 和 Looper.loop()成对存在,创建Handler必须指定looper否则会抛异常 //需要通过synchronized来保证线程安全 Looper.prepare(); synchronized (this){ myHandler = new Handler(); //myHandler创建成功通知所有等待的线程 notifyAll(); } Looper.loop(); } public Handler getHandler(){ synchronized (this) { while (null == myHandler) { try { //如果myHandler没创建成功,就需要等待,直到myHandler被创建成功 wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } return myHandler; } public void quit(){ //退出时需要清除句柄,避免内存泄露 if (mIsQuiting) return; mIsQuiting = true; if (null != myHandler){ //清除message队列及其回调 myHandler.removeCallbacksAndMessages(null); myHandler.post(new Runnable() { @Override public void run() { Looper.myLooper().quit(); mIsLoopEnd = true; } }); } } public boolean isQuiting(){ return mIsQuiting; } public boolean isLoopEnd(){ return this.mIsLoopEnd; } } //最大线程数量 private static final int MAX_TREAD_COUNT = 10; private static final int minThreadCount = 5; //当前线程数 private static int currentThreadCount = 0; //保留的线程数 private static int RETENTION_THREAD_COUNT = 10; private WorkThead[] retentionThreads; private static ThreadExtractors THREAD_POOL = null; private static Handler mainHandler = null; private ThreadExtractors(){ mainHandler = new Handler(Looper.getMainLooper()); //子线程数目为10个 retentionThreads = new WorkThead[RETENTION_THREAD_COUNT]; for(int i = 0; i < RETENTION_THREAD_COUNT; i ++){ retentionThreads[i] = newThread(); } } private WorkThead newThread(){ String threadId = UUID.randomUUID().toString(); WorkThead workThead = new WorkThead(threadId); workThead.start(); return workThead; } public void OnDestroy(){ if (null == retentionThreads) return; for (int i = 0, length = retentionThreads.length; i < length; i++){ retentionThreads[i].quit(); } mainHandler.removeCallbacksAndMessages(null); retentionThreads = null; THREAD_POOL = null; } public static void extractMainThread(Runnable runnable){ THREAD_POOL.extractMainThread(runnable, 0); } public synchronized static void extractMainThread(Runnable runnable, int delayTime){ if (null == THREAD_POOL) THREAD_POOL = new ThreadExtractors(); THREAD_POOL.mainHandler.postDelayed(runnable, delayTime); } public static void extractInThread(Runnable runnable){ THREAD_POOL.extractThread(runnable, 0); } public synchronized static void extractThread(Runnable runnable, int delayTime){ if (null == THREAD_POOL) THREAD_POOL = new ThreadExtractors(); currentThreadCount++; if(currentThreadCount >= MAX_TREAD_COUNT) currentThreadCount = 0; THREAD_POOL.retentionThreads[currentThreadCount].getHandler().postDelayed(runnable, delayTime); } private HandlerThread newHandlerThread(){ HandlerThread handlerThread = new CusmHandlerThread(UUID.randomUUID().toString()); handlerThread.start(); return handlerThread; } /** HandlerThread内部中已经封装好了线程安全,因此不必担心多线程问题 * */ public class CusmHandlerThread extends HandlerThread{ private Handler mMyHandler = null; public CusmHandlerThread(String name) { super(name); mMyHandler = new Handler(this.getLooper()); } public void setHandler(Handler handler){ mMyHandler = handler; } public Handler getHandler(){ return this.mMyHandler; } }}
对以上线程池代码进行讲解:
1、从66行 -75行可以看出,本线程池定义了1个主线程,10个子线程。
2、110行 extractMainThread(Runnable runnable, int delayTime)方法中加上同步关键字synchronized,是为了避免多线程安全问题。
3、需要注意子线程类WorkThead的定义,本线程只会创建Handler,创建Handler的时候请注意,每个Handler必须制定唯一的looper,因此如果需要自己创建非主线程的Handler,则必须将其创建放在Looper.prepare() 和 Looper.loop()之间,直到创建完成为止。
4、注意23行的getHandler()加了synchronized,是因为避免获取的时候其还没创建成功。
5、注意37行的quit()函数,退出的时候需要清理资源,避免内存泄露。
有没发现自定义线程WorkThead的实现相对来说比较繁琐,有没有更简便的方法呢?当然是有的,这就需要用到安卓封装的HandlerThread。
1、请查看129行~152行的HandlerThread定义,其功能完全可以代替WorkThead的功能,并且不用理会线程安全及其清理工作, HandlerThread内部会自动的去处理。当然HandlerThread内部也实现了线程安全问题。
下面看看以上设计的线程怎样调用:
ThreadExtractors.extractMainThread(() ->{ System.out.println("这里的代码是在UI线程执行的,本消息会在主线程的Handler的消息队列中排队处理"); }); ThreadExtractors.extractInThread(() -> { System.out.println("这里的代码是在非主线程中执行的,本消息会在非主线程的Handler的消息队列中排队处理"); });
本代码中采用了JAVA8中的新特性拉姆表达式,不熟悉的谷歌一下。
需要在主线程或者子线程执行,直接套用以上方法即可,当然每个线程post的messge会在当前线程的handler中进行排队,并且是顺序执行的。
- Android 线程池详解
- Android 线程池详解
- Android线程池详解
- Android线程池详解
- Android线程池详解
- Android 线程池详解
- Android线程池详解
- Android(线程二) 线程池详解
- Android(线程二) 线程池详解
- Java(Android)线程池详解
- Android线程池ThreadPoolExecutor详解
- Android线程池使用详解
- 详解Android线程池ThreadPoolExecutor的教程
- android线程池详解(一)
- Android线程池ThreadPoolExecutor参数详解
- Android 线程池框架、Executor、ThreadPoolExecutor详解
- Android 线程池框架、Executor、ThreadPoolExecutor详解
- Android 的线程(AsyncTask、HandlerThread、IntentService详解)和线程池
- Kotlin Reference (九) 抽象类、密封类
- HTML5 CSS3 经典案例:无插件拖拽上传图片 (支持预览与批量) (一)
- bat脚本执行带参数的sql指令(sql server)
- CenterOS-6.5 64位下载
- android zip 递归打包压缩,解压
- Android线程池详解
- Bootstrap Table总结
- React Router页面传递参数-1
- Leetcode 309. Best Time to Buy and Sell Stock with Cooldown
- 特征直方图的特征参数
- https://sqlwhisper.wordpress.com/2013/03/24/stuff-and-for-xml-path-for-string-concatenation/
- 反射再学习
- JSP&Servlet中request.getParameter() 和request.getAttribute() 区别
- 静态成员函数