Android中提供了如下几种实现多线程的方法: java
1. Thread+Handler实现多线程:
不要在本身新建的Thread里对UI进行操做。可使用Handler与主线程进行交互。有如下两种使用方式:
<1>. 发送消息: 数据库
// Handler定义在哪一个线程,就被绑定在该线程 Handler handler = new Handler() { public void handleMessage(Message msg) { super.handleMessage(msg); // 在handler所在的线程上处理消息,若是handler所在的线程是主线程,能够更新UI } }; // 新建线程Thread new Thread(new Runnable(){ public void run() { while(!Thread.currentThread().isInterrupted()) { Message msg = handler.obtainMessage(); msg.what = XX; msg.arg1 = XX; msg.obj = XX; msg.sendToTarget(); // 在新线程中分发Message对象到handler所在的线程 } } }).start();
<2>. 发送Runnable对象: 缓存
// Handler定义在哪一个线程,就被绑定在该线程 Handler handler = new Handler(); // 新建线程Thread new Thread(){ public void run(){ while(!Thread.currentThread().isInterrupted()) { handler.post(new Runnable(){ // 在新线程中分发Runnable对象到handler所在的线程中 public void run(){ // 运行在handler所在的线程,若是handler所在的线程是主线程,能够更新UI } }); } } }.start();// 新建了线程
以上例子都是在主线程中建立Handler,在子线程中引用。若是要在本身的Thread中建立Handler来与主线程交互的话,必须在new Handler前加上Looper.prepare(),在建立后加上Looper.loop()。缘由:只有主线程默认带了消息队列,自定义线程是不带MessageQueue和Looper的。 安全
<3>.Thread中断问题: 多线程
虽然有stop()接口,可是不安全,会发生异常。因此,经过调用interrupt()设置中断标示,在Thread的run()函数中,若是有循环语句,在循环中经过断定isInterrupted(),若是为true,主动退出run()函数便可结束线程,达到中断的目的。 并发
<4>. 一组线程的执行是无序的,能够实现协同工做,也能够各自独立工做。 框架
// 多个线程共享同一个TaskRun的实例,即实现协做工做。
TaskRun task = new TaskRun(); //TaskRun实现Runnable接口的run()函数 异步
new Thread(task,"t1").start();
new Thread(task,"t2").start();
// 多个线程各自初始化各自的实例,即各自作各自的工做。
MyThread myT1 = new MyThread(); //MyThread继承了Thread,本身实现run()函数
MyThread myT2 = new MyThread();
myT1.start();
myT2.start(); ide
2. AsyncTask 异步任务: 函数
一个实例仅执行一次,屡次execute会抛出异常。因此,要执行几个任务就new几个实例。内部会建立一个进程做用域的线程池来管理要运行的任务。
2.3平台之前调用execute,全部的任务并发执行,内部的线程池限制是5个,后来默认串行执行,只能调用executeOnExecutor((ExecutorService)Executors.newCachedThreadPool())实现并发,这里没有使用默认的线程池,而使用自定义的。
适用于短期的多线程任务(几分钟)。若是要完成长时间的运行任务,最好使用Executor,ThreadPoolExecutor,FutureTask。
<1>.AsyncTask<Params, Progress, Result>
是抽象类,定义了三种泛型类型,分别表明“启动任务执行的输入参数”(如HTTP请求的URL)、“后台任务执行的进度”、“后台计算结果的类型”。若是没有被使用,能够用Void代替。
<2>.AsyncTask定义的方法:
execute(Params... params),执行一个异步任务,须要咱们在代码中调用此方法,触发异步任务的执行。
onPreExecute(),在execute(Params... params)被调用后当即执行,通常用来在执行后台任务前对UI作一些标记。
doInBackground(Params... params),在onPreExecute()完成后当即执行,用于执行较为费时的操做,此方法将接收输入参数和返回计算结果。不能更新UI,只能在执行过程当中调用publishProgress(Progress... values)来更新进度信息。
onProgressUpdate(Progress... values),在调用publishProgress(Progress... values)时被执行,将进度信息更新到UI组件上。
onPostExecute(Result result),当后台操做结束时,此方法将会被调用,计算结果将作为参数传递到此方法中,直接将结果显示到UI组件上。
<3>.终止线程:
cancel()终止后台线程的运行,能够先用isCanceled()来询问,避免重复叫停。
执行cancel(),将会回调onCanceled(),同时onPostExecute()就不会被回调。若是想作到及时中断,在doInBackground()中,检测该状态位来判断是否继续运行。
在 Java 中,非静态匿名内部类会持有其外部类的隐式引用,该引用会致使 Activity 被保留,而不是被垃圾回收机制回收。将线程类(Thread,AsyncTask)声明为私有的静态内部类避免了 Activity context 的内存泄漏问题,但在配置发生改变后,线程仍然会执行。缘由在于,DVM 虚拟机持有全部运行线程的引用,不管这些线程是否被回收,都与 Activity 的生命周期无关。运行中的线程只会继续运行,直到Android 系统将整个应用进程杀死。在退出当前Activity 前使用 onDestroy() 方法结束运行中线程是个不错的选择。
private MyTask task; private static int i = 1; private static class MyTask extends AsyncTask<Void, Void, Integer>{ @Override protected Integer doInBackground(Void... params) { while(!isCancelled()) { i++; Log.d("i = ", String.valueOf(i)); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } return null; } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); task = new MyTask(); task.execute(); } @Override protected void onDestroy() { super.onDestroy(); if(task!=null && task.getStatus()==Status.RUNNING) { task.cancel(true); } }
3. 线程池:
Java经过Executors提供的四种线程池,分别为:
<1>. newCachedThreadPool建立一个可缓存线程池,调用 execute 将重用之前构造的线程(若是线程可用),若无可回收,则新建线程。
有IDLE机制,线程若是超过TIMEOUT(60s)不活动,其会自动被终止。一般用于执行一些生存期很短的异步型任务。
<2>. newFixedThreadPool 建立一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
没有IDLE机制,在某个线程被显式地关闭以前,池中的线程将一直存在。多数针对一些很稳定很固定的正规并发线程。
定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()
<3>. newScheduledThreadPool 建立一个定长线程池,支持定时及周期性任务执行。
如下是3种定时器的写法与比较:
Handler.postDelayed其实是运行在主线程的,也能够新建一个Thread来发送Runnable。
在Timer机制中,只封装了一个线程来执行定时任务,并且有时有异常发生。
ScheduledExecutorService比Timer更安全,功能更强大,是最优化方案。
private static final int REGUEST_MESSAGE_TIME = 1000*60*60*2;//2小时 //使用Handler做为定时器,实际上Runnable仍是在主线程中运行 private Handler mTimerHandler = new Handler(); private Runnable mTimerRunnable = new Runnable() { @Override public void run() { // 2小时后执行一次Runnable msgTimerHandler.postDelayed(this, REGUEST_MESSAGE_TIME); } }; // Timer + TimerTask定时器,在Timer机制中,只有一个线程来执行定时任务 private Timer mTimer = null; private TimerTask mTimerTask = new TimerTask() { @Override public void run() { } }; // 使用线程池的方式支持定时线程任务 private ScheduledExecutorService mScheduledExecutorService = null; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 立刻执行Runnable msgTimerHandler.post(mTimerRunnable); mTimer = new Timer(); // 每隔2小时执行一次TimerTask,循环任务 mTimer.schedule(mTimerTask, 0, REGUEST_MESSAGE_TIME); // 该线程池能够容纳5个线程 mScheduledExecutorService= Executors.newScheduledThreadPool(5); // 立刻执行Runnable ,而后每隔2小时执行一次Runnable mScheduledExecutorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { } }, 0, 2, TimeUnit.HOURS); } protected void onDestroy() { super.onDestroy(); // 清除以该Handler为target的全部Message和Callback,防止内存泄露 msgTimerHandler.removeCallbacksAndMessages(null); if ( mTimer != null ) { //不但能够结束当前schedule,连整个Timer的线程都会结束掉 mTimer.cancel(); mTimer = null; } //关闭线程池队列中的全部等待任务 mScheduledExecutorService.shutdown(); }
<4>. newSingleThreadExecutor 建立一个单线程化的线程池,它只会用惟一的工做线程来执行任务,保证全部任务按照指定顺序(FIFO, LIFO, 优先级)执行。 不用考虑同步的问题。
没有IDLE机制。可用于数据库操做,文件操做,应用批量安装,应用批量删除等不适合并发但可能IO阻塞性及影响UI线程响应的操做。
好处:
a. 重用存在的线程,减小对象建立、消亡的开销,性能佳。
b. 可有效控制最大并发线程数,提升系统资源的使用率,同时避免过多资源竞争,避免堵塞。
c. 提供定时执行、按期执行、单线程、并发数控制等功能。
开启线程的两种方法:
Future<?> submit(Runnable task) /*用Future来判断线程运行状态*/
void execute(Runnable command) /*没法判断线程是否成功完成*/
关闭线程池的方法:
shutdown() 中止接受新任务且等待已经提交的任务执行完成(已经提交的任务会分两类:一类是已经在执行的,另外一类是尚未开始执行的),当全部已经提交的任务执行完毕后将会关闭ExecutorService。
shutdownNow() 试图中止全部正在执行的活动任务(调用对应线程的interrupt(),设置中断标志位而已),暂停处理正在等待的任务,并返回等待执行的任务列表。
中断线程池某个任务:
向ExecutorService提交任务调用submit方法以后,返回值是个Future对象,可使用这个对象的cancel方法来取消对应任务。
若是任务还在等待队列中,会直接取消掉。
若是任务已经执行了,cancel的boolean值参数mayInterruptIfRunning用于表示当任务已经开始执行时,是否须要尝试中断执行该任务的线程。(注意:这只是表示任务是否可以接收中断,而不是表示任务是否能检测并处理中断)
因此仍是得在本身的任务中经过断定中断标志位来结束任务。
建议:
与主线程有交互时用AsyncTask,不然就用Thread。
当有须要大量线程执行任务时,必定要建立线程池,好比批量下载图片或文件。
对于想要当即开始执行的异步任务,要么直接使用Thread,要么单首创建线程池提供给AsyncTask,默认的AsyncTask不必定会当即执行你的任务。
4. 访问临界资源时,使用线程同步机制:
synchronized关键字能够修饰方法,也能够修饰代码块,但不能修饰构造器,属性等。
5. 处理线程阻塞中断:
在执行涉及线程调度的阻塞调用时(例如wait、sleep和join),若是发生中断,被阻塞线程会“尽量快的”抛出InterruptedException。所以,咱们就能够用下面的代码框架来处理:
try {
//wait、sleep或join
}
catch(InterruptedException e) {
//某些中断处理工做
}
<1>.sleep()静态方法
在指定时间内让当前正在执行的线程暂停执行,但不会释放“锁标志”。
使当前线程进入阻塞状态,在指定时间内不会执行。其余线程在此期间能够得到运行机会。线程睡眠到期自动苏醒,并返回到可运行状态,不是运行状态。什么时候运行还要看线程池如何调度。
<2>.wait()方法
在其余线程调用对象的notify或notifyAll方法前,致使当前线程等待。线程会释放掉它所占有的“锁标志”,从而使别的线程有机会抢占该锁。
当前线程必须拥有当前对象锁。若是当前线程不是此锁的拥有者,会抛出IllegalMonitorStateException异常。
唤醒当前对象锁的等待线程使用notify或notifyAll方法,也必须拥有相同的对象锁,不然也会抛出IllegalMonitorStateException异常。
wait()和notify()必须在synchronized函数或synchronized block中进行调用。若是在non-synchronized函数或non-synchronized block中进行调用,虽然能编译经过,但在运行时会发生IllegalMonitorStateException的异常。
<3>.yield静态方法
暂停当前正在执行的线程对象,并执行其余线程。
yield()只是使当前线程从新回到可执行状态,因此执行yield()的线程有可能在进入到可执行状态后立刻又被执行。
yield()只能使同优先级或更高优先级的线程有执行的机会。
<4>.join方法
保证当前线程中止执行,直到该线程所加入的线程完成为止。然而,若是它加入的线程没有存活,则当前线程不须要中止。
如在t1线程中调用t2.join(),则须要t2线程执行完后t1方能继续执行。