目录:
[TOC]
线程
-
什么是线程?
- 线程是一种轻量级进程,大多数情况下用于执行异步操作。
- 一个Android 程序开始运行的时候,会单独启动一个进程,同时会产生一个UI Thread线程。
- 一个Android 程序默认情况下也只有一个Process,但一个Process下却可以有许多个Thread。
-
线程与进程的区别
- 地址空间:
- 线程是进程内的一个执行单元,一个进程至少有一个线程;
- 线程共享进程的地址空间,而进程有自己独立的地址空间;
- 资源拥有:
- 进程是资源分配和拥有的单位;
- 同一个进程内的线程共享进程的资源;
- 线程是处理器调度的基本单位,但进程不是;
- 线程只需要很少的资源就可“轻装上阵”运行的优点,来弥补进程并发的“颗粒度”粗糙的缺点,提高系统资源利用率。
- 地址空间:
-
为什么使用线程?
- 在Android中线程分为主线程(UI线程)和子线程,主线程主要处理和界面相关的事情,而子线程则执行耗时操作。
- 如果在主线程执行耗时操作时,如网络请求或数据库读取,就会阻塞主线程 其他逻辑的执行,导致程序无法及时响应从而导致界面卡顿。
- 如果卡顿时间超过5秒,系统就会报ANR错误。所以,如果要执行耗时的操作,需要另起线程执行。
-
注意:
从Android 3.0 开始系统要求网络访问必须在子线程中进行,否则网络访问将失败并抛出NetworkOnMainThreadException异常,这样做是为了避免主线程由于耗时操作所阻塞从而出现ANR现象。
-
线程的实现方法
-
方法一:实现Thread
-
扩展 java.lang.Thread 类
Thread mThread=new Thread(new Runnable() { @Override public void run() { // method } }); mThread.start();
-
实现 Runnable 接口
public class ThreadTest extends Activity implements Runnable { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Thread thread=new Thread(this); thread.start(); } @Override public void run() { // mothod } }
-
继承 Thread 类与实现 Runnable 接口的区别
- 在 Java API中,Thread是实现了Runnable接口;
- 在 Java 中,类仅支持单继承,也就是说,当定义一个新的类的时候,它只能扩展一个外部类;
- 使用实现Runnable接口的方式创建的线程可以处理同一资源,从而实现资源的共享。
-
-
方法三:实现AsyncTask
AsyncTask是一种轻量级的异步任务类,它可在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新UI。
AsyncTask封装了Thread和Handler,更加方便执行后台任务以及在主线程中访问UI,但不适合进行特别耗时的后台任务。
AsyncTask是一个抽象的泛型类,提供了Params(参数类型)、Progess(后台任务的执行速度)和Result(后台任务的返回结果的类型)这三个泛型参数。- onPreExecute()
开始执行前的准备工作; - doInBackground(Params...)
开始执行后台处理,可以调用publishProgress方法来更新实时的任务进度; - onProgressUpdate(Progress...)
在publishProgress方法被调用后,UI 线程将调用这个方法从而在界面上展示任务的进展情况; - onPostExecute(Result)
执行完成后的操作,传送结果给UI线程。 - onCancelled()
在主线程中执行,任务被取消时调用,这个时候onPostExecute()则不会调用。 - 注意:
- AsyncTask的对象必须在UI线程中创建;
- AsyncTask.execute()必须在UI线程中调用;
- 除了doInBackground(Params...),其余方法都是被UI线程所调用的;
- 一个AsyncTask对象只能被执行一次,即只能调用一次execute(),否则 将会出现运行时异常;
- 这些方法都不能直接调用;
- 不能手动停止。
- Android版本区别
- 在Android 1.6 之前,AsyncTask是串行执行任务的。
- 在Android 1.6 的时候,AsyncTask采用线程池里处理并行任务。
- 从Android 3.0 开始,为了避免AsyncTask所带来的并发错误,AsyncTask又采用一个线程来串行执行任务。但,可以通过AsyncTask的executeOnExecutor()来并行地执行任务。
- onPreExecute()
-
方法四:实现IntentService
IntentService继承了Service并且它是一个抽象类,IntentService封装了HandlerThread和Handler,用来处理异步请求,可执行后台耗时任务。
IntentService通过worker thread处理每个Intent对象,执行完所有工作后自动停止Service。
IntentService优先级比单纯的线程要高很多,所以适合执行高优先级的后台任务,因为不容易被系统杀死。
-
public class IntentServiceTest extends IntentService {
public IntentServiceTest() {
super("IntentServiceTest");
}
@Override
public IBinder onBind(Intent intent)
{
return null;
}
@Override
public void onCreate()
{
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId)
{
return super.onStartCommand(intent, flags, startId);
}
@Override
protected void onHandleIntent(Intent intent)
{
try {
//耗时操作
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void onDestroy()
{
super.onDestroy();
}
}
// 启动的IntentService
Intent intent = new Intent(this, IntentServiceTest.class);
startService(intent);
startService(intent);
startService(intent);
* 方法五:实现HandlerThread
HandlerThread继承Thread,是一种可以使用Handler的Thread,通过run方法中Looper.prepare()来创建消息队列,并通过Looper.loop()来开启消息循环。
由于HandlerThread的run方法是一个无限循环,在不使用时,需通过quit或者quitSafely方法来终止线程执行。
public class HandlerThreadActivity extends Activity {
private static final int SIGN = 1;
private HandlerThread mHandlerThread;
private Handler mMsgHandler;
private boolean isContinue;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_threadHandler);
//创建后台线程
initThread();
}
@Override
protected void onResume()
{
super.onResume();
//继续
isContinue = true;
mMsgHandler.sendEmptyMessage(SIGN);
}
@Override
protected void onPause()
{
super.onPause();
//停止继续
isContinue = false;
mMsgHandler.removeMessages(MSG_UPDATE_INFO);
}
private void initThread()
{
mHandlerThread = new HandlerThread("handlerThread");
mHandlerThread.start();
mMsgHandler = new Handler(mCheckMsgThread.getLooper())
{
@Override
public void handleMessage(Message msg)
{
try {//模拟请求
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (isContinue) {
mMsgHandler.sendEmptyMessageDelayed(SIGN, 1000);
}
}
};
}
@Override
protected void onDestroy()
{
super.onDestroy();
//释放资源
mHandlerThread.quit();
}
}
琐机制
Looper、Handler、MessageQueue、Message
-
Message
消息体,封装了传输消息所需的数据结构。 -
Handler
Message的主要处理者,负责Message的发送,Message内容的执行处理。-
Handler会向message queue通过sendMessage或post两种方法发送消息
- 都会插在message queue队尾并按先进先出执行
- 通过sendMessage发送的是一个message对象,会被Handler的handleMessage()处理;
- 通过post方法发送的是一个runnable对象,则会自己执行。
-
使用Handler注意事项:
- 创建massage对象时,推荐使用obtain()方法获取,因为Message内部会维护一个Message池用于Message的复用,这样就可以避免 重新new message而冲内心分配内存,减少new 对象产生的资源的消耗。
- handler 的handleMessage方法内部如果有调用外部activity或者fragment的对象,一定要用弱饮用,handler最好定义成static的,这样可以避免内存泄漏;
为什么呢?因为一但handler发送了消息。而handler内部有对外部变量的引用,此时handler已经进入了looper的messageQueue里面。此时activity或者fragment退出了可是区域,但是handler内部持有其引用且为强引用时,其就不会立即销毁,产生延迟销毁的情况; - 不确定当前线程时,更新UI时尽量调用post方法。
-
-
Lopper
循环器,负责管理一个消息循环队列(MessageQueue)的。 -
MessageQueue
消息队列,用来存放handler发布的消息,按照先进先出执行。 -
Thread、Lopper、Handler、MessageQueue 间关系
-
一个 Thread 对应一个 Looper;
-
一个 Looper 对应一个 MessageQueue;
-
一个 Looper 可以对应多个 Handler;
-
一个 Thread 可以有多个Handler;
-
一个 Handler 只能和一个Thread 关联;
-
Handler 会引用当前线程里的特定 Looper 和 MessageQueue;
-
Handler 的处理过程运行在创建 Handler 的 Thread 里。
class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); //创建本线程的Looper并创建一个MessageQueue mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop(); //开始运行Looper,监听Message Queue } }
-
操作多线程的方式
- Handler + Thread
- AsyncTask
- ThreadPoolExecutor
- IntentService
子线程更新UI的方式
- View.post(Runnable action)
- Activity.runOnUiThread(Runnable action)
- AsyncTask
- Handler
线程池
-
什么是线程池?
线程池能够对线程进行简单管理,因为线程不可能无限制的产生,并且线程的创建和销毁都会有相应的开销。
线程池中会缓存一定数量的线程,通过这个线程池就可以避免频繁创建和销毁线程带来的系统开销。- 优点
- 重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销;
- 能够有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象。
- 优点
-
ThreadPoolExecutor
ThreadPoolExecutor提供一系列参数来配置线程池,通过不同参数可以创建不同的线程池。-
构造函数
- int corePoolSize
线程池维护线程的最少数量。
默认情况下,核心线程数会在线程池中一直存活,即使处于闲置状态。如果将ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为ture,那么闲置的核心线程在等待新任务到来时会有超时策略,这个时间间隔由keepLiveTime指定。当等待时间超过keepLiveTime,核心线程就会被终止。 - int maximumPoolSize
线程池维护线程的最大数量。
活动线程达到这个数量,后续的新任务将会被阻塞。 - long keepAliveTime
线程池维护线程所允许的空闲时间,超过这个时长,线程就会被回收。 - TimeUnit unit
线程池维护线程所允许的空闲时间的单位,是一个枚举。 - BlockingQueue<Runnable> workQueue
线程池所使用的缓冲队列。
通过线程池的execute方法提交的Runable对象会存储在这个参数中。 - ThreadFactory threadFactory
线程池用于创建线程。
是一个接口,只有一个方法Thread newThread(Runnable r)。 - RejectedExecutionHandler handler
线程池对拒绝任务的处理策略。
当线程池无法执行新任务时,可能是由于任务队列已满或者是无法成功执行任务,这个时候handler的rejectedExecution方法来通知调用者。 - 配置参考(AsyncTask)
- 核心线程数等于CPU核心数+1;
- 最大线程数等于CPU核心数的2倍+1;
- 核心线程无超时机制,非核心线程在闲置时的超时时间为1秒;
- 任务队列的容量为128。
- int corePoolSize
-
线程创建规则
ThreadPoolExecutor对象初始化时,不创建任何执行线程,当有新任务进来时,才会创建执行线程。- 当目前执行线程的总数小于核心线程大小时,所有新加入的任务,都在新线程中处理。
- 当目前执行线程的总数大于或等于核心线程时,所有新加入的任务,都放入任务缓存队列中。
- 当目前执行线程的总数大于或等于核心线程,并且缓存队列已满,同时此时线程总数小于线程池的最大大小,那么创建新线程,加入线程池中,协助处理新的任务。
- 当所有线程都在执行,线程池大小已经达到上限,并且缓存队列已满时,就handler拒绝新的任务。
-
线程池分类
- FixedThreadPool
通过Executors的newFixedThreadPool方法创建。
线程数目固定且只有核心线程的线程池,线程处于空闲状态时,并不会被回收,除非线程池关闭。
当所有线程都处于活动状态时,新任务都会处于等待状态,直到有线程空闲出来。- 优点:只有核心线程并且不会被回收,能够更加快速的响应外界的请求。
- CachedThreadPool
通过Executors的newCachedThreadPool方法创建。
线程数目不固定且只有非核心线程的线程池,并且最大线程数为Intager.MAX_VALUE。
当所有线程都处于活动状态时,线程池会创建新的线程来处理新任务,否则就会利用空闲的线程来处理新任务。空闲线程有超时机制,超时时长为60秒,超过就会被回收。- 优点:适合执行大量的耗时较少的任务,当线程池处于闲置状态,线程都会超时停止,不占用系统资源。
- ScheduledThreadPool
通过Executors的newScheduledThreadPool方法创建。
核心线程数固定,非核心线程数不固定的线程池,并且当非核心线程闲置时立刻回收。- 优点:用于执行定时任务和具有固定周期的重复任务。
- SingleThreadExecutor
通过Executors的newSingleThreadExecutor方法创建。
只有一个核心线程的线程池,确保所有任务都在同一个线程中按顺序执行。- 优点:统一将外界任务到一个线程中,不需要处理线程同步问题。
- FixedThreadPool
-
线程池执行
- execute()中,调用了三个私有方法
- addIfUnderCorePoolSize()
在线程池大小小于核心线程池大小的情况下,扩展线程池 - addIfUnderMaximumPoolSize()
在线程池大小小于线程池大小上限的情况下,扩展线程池 - ensureQueuedTaskHandled()
保证在线程池关闭的情况下,新加入队列的线程也能正确处理
-
线程池关闭
- shutdown()
不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务 - shutdownNow()
立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务
- shutdown()
-
网友评论