Android多线程
一、消息机制
1.概要
Android应用启动时会有一个默认的主线程(UI线程),主线程中关联一个消息队列,所有操作都被封装成消息交给主线程处理,将获取消息的操作放在死循环中。
开启子线程执行耗时操作后,发消息(sendMessage(msg)或者post Runnable(callback)/Message——》把Message加入消息队列)给主线程的Handler,Handler从消息队列中取消息,通过handMessage更新UI或者其他处理。
其中,消息队列封装在Looper里,Looper与thread关联,handler与looper关联,即handler与thread建立了关联。handler处理消息用handleCallback/handleMessage。即handler读消息后又处理消息。
示例:
class MYHandler extends Handler
{
public void handleMessage(Message msg)
{
//更新UI
}
}
主线程中:
MyHandler mHandler=new MyHandler();
new Thread()
{
public void run()
{
//耗时操作
mHandler.sendEmptyMessage(123);
};
}.start();
2.Android源码分析
UI线程的消息循环在ActivityThread.main方法中创建,该函数为Android应用程序的入口。
public static void main(String[] args)
{
…………
//创建消息循环looper
Looper.prepareMainLooper();
ActivityThread thread=new ActivityThread ();
thread.attach(false);
if(sMainThreadHanlder==null)
{
//UI线程的handler
sMainThreadHanlder=thread.getHanlder();
}
……
Looper.loop();//执行消息循环
……
}
Handler的默认构造函数:
public Handler()
{
……
mLooper=Looper.myLooper();//获取looper
if(mLooper ==null)
throw new RuntimeException();
mQueue=mLooper.mQueue;//获取消息队列
mCallback=null;
}
Handler的其他函数:
public static Looper myLooper()
{
return sThreadLocal.get();
}
//设置UI线程的Looper
public static void prepareMainLooper()
{
prepare();
setMainLooper(myLooper());
myLooper().mQueue.mQuitAllowed=false;
}
private synchronized static void setMainLooper(Looper looper)
{
mMainLooper=looper;
}
//为当前线程设置looper
public static void prepare()
{
if(s.ThreadLocal.get!=null)
{
throw …………
}
sThreadLocal.set(new Looper());
}
//执行消息循环
public static void loop()
{
Looper me=myLooper();
if(me ==null)
throw……
MessageQueue queue=me.mQueue;//获取消息队列
……
//死循环
while(true)
{
//获取消息
Message msg=queue.next();
if(msg!=null)
{
if(msg.target==null) return;
}
//处理消息
msg.target.dispatchMessage(msg);
msg.reclycle();//回收消息
}
}
//消息处理函数,子类覆写
public void handleMessage(Message msg){}
private final void handleCallback(Message msg)
{
msg.callback.run();
}
/**
分发消息
post时callback不为空,用handleCallback()处理,sendMeessage的时候一般不设置callback,用handleMessage()处理。*/
public void dispatchMessage(Messgae msg)
{
if(msg.callback!=null) {handleCallback(msg)}
else
{
if(mCallback!=null)
if(mCallback.handleMessage(msg)) return;
}
handleMessage(msg);
}
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r),0);
}
//将Runnable包装成Message对象,将Runnable对象设置给Message对象的callback字段。
private final Message getPostMessage(Runnable r)
{
Message m=Message.obtain();
m.callback=r;
return m;
}
public final boolean sendMessageDelayed(Message msg,long delayMillis)
{
if(delayMillis<0)
{delayMillis=0;}
return sendMessageAtTime(msg,SystemClocl.uptimeMillis()+delayMillis);
}
//将Message对象插入到消息队列
public boolean sendMessageAtTime(Message msg,long uptimeMillis)
{
boolean sent=false;
MessageQueue queue=mQueue;
if(queue!=null)
{
msg.target=this;
sent=queue.enqueueMessage(msg,uptimeMillis);
}…………
}
创建handler的时候looper一定不能为空。
所以在子线程中创建handler对象时:
new Thread()
{
Handler handler=null;
public void run()
{
Looper.prepare():
handler=new Handler();
Looper.loop();
}
}.start();
二、Android中的多线程
1.多线程的实现——Thread和Runnable
启动新线程:
① private void startNewThread()
{
new Thread()
{
public void run(){//耗时操作}
}.start();
}
② private void startNewThread()
{
new Thread(new Runnable()
{
public void run(){//耗时操作}
} ).start();
}
实际上thread也是一个runnable,他实现了runnable接口。thread类中有一个Runnable类型的target字段,代表要被执行在这个子线程中的任务。最终被执行的是 Runnable,thread是对Runnable的包装。当启动一个线程时,如果thread的target不为空,则在子线程中执行这个target的run函数,否则执行thread自身的run函数。
2.线程的wait、sleep、join、yield
函数名 |
作用 |
wait() |
当线程执行到wait()方法时,进入等待池中,失去对象的机锁,其他线程可以访问对象。 可以用notify、notifyAll或者指定睡眠时间来唤醒等待池中的线程。 wait()、notify()、notifyAll()必须放在synchronized 块中,否则会抛出异常 |
sleep |
thread的静态函数,使调用线程进入睡眠状态,不能改变对象的机锁,其他线程无法访问这个对象 |
join |
等待目标线程执行完成后再继续执行,即阻塞当前调用join函数所在的线程等接受线程执行完毕后再继续。 |
yield |
目标线程让出执行权限,进入就绪状态,让其他线程优先执行,但是其他线程未必能优先执行 |
wait、notify、sleep示例:
private static Object sLockObject =new Object();
static void waitAndNotifyAll()
{
Thread thread=new WaitThread();
thread.start();
long startTime=System.currentTimeMillis();
try{
synchronized (sLockObject)
{
SLockObject.wait();
}
}catch(Exception e){}
long timeMS=(System.currentTimeMillis()-startTime);
}
static class WaitThread extends Thread
{
public void run()
{
try
{
synchronized(sLockObject)
{
Thread.sleep(3000);
sLockObject.notifyAll();
}
}catch(Exception e){}
}
}
join的示例:
static void joinDemo()
{
Worker worker1=new Worker(“work-1”);
Worker worker2=new Worker(“work-2”);
System.out.println(“启动线程1”);
worker1.start();
try
{
worker1.join();//主线程阻塞到worker1执行完成
System.out.println(“启动线程2”);
worker2.start();
worker2.join();//主线程阻塞到worker2执行完成
}catch(InterruptedException e){……}
System.out.println(“主线程继续执行”);
}
static Class Worker extends Thread{
public Worker(String name){super(name);}
public void run()
{
try
{
Thread.sleep(2000);
}catch(InterruptedException e){……}
System.out.println(“work in”+getName());
}
}
执行结果:
启动线程1
work in work-1
启动线程2
work in work-2
主线程继续执行
yield示例:
static class YieldThread extends Thread
{
public YieldThread(String name){super(name);}
public synchronized void run()
{
for(int i=0;i<MAX;i++)
{
System.out.println(“%s[%d]---->%d\n”,this.getName(),this.getPriority(),i);
if(i==2)
Thread.yield();
}
}
}
static void yieldDemo()
{
YieldThread t1=new YieldThread (“thread-1”);
YieldThread t2=new YieldThread (“thread-2”);
t1.start();
t2.start();
}
执行结果:
thread-1,优先级为:5 ---->0
thread-1,优先级为:5 ---->1
thread-1,优先级为:5 ---->2
thread-2,优先级为:5 ---->0
thread-2,优先级为:5 ---->1
thread-2,优先级为:5 ---->2
thread-1,优先级为:5 ---->3
thread-1,优先级为:5 ---->4
thread-2,优先级为:5 ---->3
thread-2,优先级为:5 ---->4
3.Callable、Future、FutureTask
callable\future\futuretask只能运用到线程池中,runnable既然运用在thread中又能运行在线程池中。
① callable
泛型接口,有一个返回值为V的call()函数。runnable的run()函数不能返回结果。
②future
接口,为线程池制定了可管理的任务标准,提供了对runnable或者callable任务的执行结果进行取消、查询是否完成、获取结果、设置结果操作。
③futuretask
是future的实现类,实现了runnable和future<v>两个接口。最终都是执行callable类型的任务。
示例:
public class FutureDemo
{
static ExecutorService mExe=Executors.newSingleThreadExecutor();//线程池
public static void main(String[] args)
{
try
{
futureWithRunnable();
futureWithCallable();
futureTask();
}catch……
}
private static void futureWithRunnable()throws InterruptedException,ExecutionException
{
Future<?> result=mExe.submit(new Runnable()
{
public void run()
{
fibc(20);
}
});
System.out.println(“future result from runnable”+result.get());
}
private static void futureWithCallable()……
{
Future<Integer>result2=mExe.submit(new Callable<Integer>()
{
public Integer call()throws Exception{return fibc(20);}
});
System.out.println(“future result from callable”+result2.get());
}
private static void futureTask()……
{
FutureTask<Integer>futureTask=new FutureTask<Integer>
(new Callable<Integer>()
{
public Integer call()throws ……{return fibc(20);}
});
mExe.submit(futureTask);
System.out.println(“future result from futureTask”+futureTask.get());
}
//斐波那契数列
private static int fibc(int num)
{
if(num==0) return 0;
if(num==1)return 1;
return fibc(num-1)+fibc(num-2);
}
}
执行结果:
future result from runnable: null
future result from callable: 6765
future result from futureTask:6765
4.线程池
线程池会创建多个线程并进行管理,提交给线程的任务会被线程池指派给其中的线程进行执行,通过线程池的统一调度,有效地管理、调度线程,避免过多的资源消耗。优点如下:
(1)重用存在的线程,减少对象创建、销毁的开销。
(2)可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
(3)提供定时执行、定期执行、单线程、并发数控制等功能。
线程池实现了ExecutorService接口。利用JDK的Executors工厂类来创建线程池。
①启动指定数量的线程——ThreadPoolExecutor
启动指定数量的线程以及将任务添加到一个队列中,并将任务分发给空闲的线程。创建后便进入运行状态,调用了shutdown()进入关闭状态,不再接受新的任务,但是还在执行已经提交了的任务,当所有提交的任务执行完了之后就变成终止状态。
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler),参数详细说明如下表。
参数名 |
作用 |
corePoolSize |
线程池中保存的核心线程数 |
maximumPoolSize |
允许创建的最大线程数。当线程数大于核心线程数小于最大线程数时, |
keepAliveTime |
终止多余的空闲线程的时间 |
unit |
时间单位,可选值为毫秒、秒、分等 |
workQueue |
任务队列,当线程池达到核心线程数,且所有线程都处于活动状态时将新加的任务放到队列中。 |
threadFactory |
线程工厂 |
handler |
拒绝策略,当线程池与任务队列都满了的时候,对新加任务采取的处理策略 |
workQueue:
(1)ArrayBlockingQueue:基于数组结构的有界数列,先入先出对任务进行排序。如果队列满了还有任务进来,则调用拒绝策略。
(2)LinkedBlockingQueue:基于链表结构的无界队列,忽略拒绝策略、最大线程数等参数。
(3)SynchronousQueue:将任务提交给线程而不是加入到新队列,每个插入的操作必须等到另一个调用移除的操作,如果新任务来了线程池没有任何可用线程的话,则调用拒绝策略。
(4)PriorityBlockingQueue:具有优先级的有界队列。
拒绝策略:
(1)AbortPolicy:拒绝任务,抛出RejectedExecutionException异常。默认策略
(2)CallerRunsPolicy:拒绝新任务进入,如果线程池还没关闭,将新任务执行在调用线程中。
(3)DiscardOldestPolicy:如果执行程序还没关闭,则位于队列头部的任务将被删除,然后重试执行程序。
(4)DiscardPolicy:加不进的任务都被抛弃了,同时没有异常抛出。
未完待续