消息機(jī)制
處理消息的手段--Handler,Looper與MessageQueue
=tips:=
子線程無(wú)法更新UI,所以需要通過(guò)Handler將一個(gè)消息Post到UI線程中(該Handler必須在主線程中創(chuàng)建),
為什么?
每個(gè)Handler都會(huì)關(guān)聯(lián)一個(gè)消息隊(duì)列,消息隊(duì)列被封裝在Looper中,每個(gè)Looper又回關(guān)聯(lián)一個(gè)線程(Looper通過(guò)ThreadLocal封裝),最終就等于每個(gè)消息隊(duì)列會(huì)關(guān)聯(lián)一個(gè)線程。Handler就是一個(gè)消息處理器,將消息投遞給消息隊(duì)列,然后再由對(duì)應(yīng)的線程從消息隊(duì)列中逐個(gè)取出消息,并且執(zhí)行。默認(rèn)情況下,消息隊(duì)列只有一個(gè),即主線程的消息隊(duì)列,這個(gè)消息隊(duì)列是在ActivityThread.main方法中創(chuàng)建的,通過(guò)Looper.prepareMainLooper()來(lái)創(chuàng)建,最后執(zhí)行Looper.loop()來(lái)啟動(dòng)消息循環(huán)。
那么Handler是如何關(guān)聯(lián)消息隊(duì)列以及線程的呢?
Handler會(huì)在內(nèi)部通過(guò)Looper.myLooper()來(lái)獲取Looper對(duì)象,并且與之關(guān)聯(lián),最重要的就是消息隊(duì)列消息隊(duì)列通過(guò)Looper與線程關(guān)聯(lián)上,Handler與Looper關(guān)聯(lián);
Handler要與主線程的消息隊(duì)列關(guān)聯(lián)上,這樣handlerMessage才會(huì)執(zhí)行在UI線程,此時(shí)更新UI才是線程安全的!消息循環(huán)的建立是通過(guò)Looper.loop()這個(gè)方法。
Looper總結(jié):
通過(guò)Looper.prepare()來(lái)創(chuàng)建Looper對(duì)象(消息隊(duì)列封裝在Looper對(duì)象中),并且保存在sThreadLoal中,然后通過(guò)Looper.loop()來(lái)執(zhí)行消息循環(huán)。
Handler最終將消息追加到MessageQueue中,而Looper不斷地從
MessageQueue中讀取消息,并且調(diào)用Handler的dispatchMessage消息,
這樣消息就源源不斷地被產(chǎn)生,添加到MessageQueue,被handler處理,
這樣Android引用就運(yùn)轉(zhuǎn)起來(lái)了
在子線程中創(chuàng)建Handler為何會(huì)拋出異常?
new Thread(){
Handler handler = null;
public void run(){
handler = new Handler();
}
}.start();
上述代碼的問(wèn)題?
在Handler源碼中,Looper對(duì)象是ThreadLocal的,每個(gè)線程都有自己的Looper,Looper可以為空,但是當(dāng)子線程中創(chuàng)建Handler對(duì)象時(shí),如果Looper為空,那么就會(huì)拋出異常
Handler源碼中有判斷:
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
修改:
new Thread(){
Handler handler = null;
public void run(){
//1,為當(dāng)前線程創(chuàng)建Looper,綁定到ThreadLocal中
Looper.prepare();
handler = new Handler();
//2,啟動(dòng)消息循環(huán)
Looper.loop();
}
}.start();
多線程
Android中的多線程就是JAVA中的多線程,Java中的線程詳解:http://www.cnblogs.com/riskyer/p/3263032.html
為了方便,Android封裝了一些類,如:AsyncTask,HandlerThread等。
Runnable和Thread有什么區(qū)別?
看Thread的源碼,Thread實(shí)現(xiàn)了Runnable接口,Thread里最終被線程執(zhí)行的任務(wù)是Runnable,而非Thread。
Thread只是對(duì)Runnable的包裝,并且通過(guò)一些狀態(tài)對(duì)Thread進(jìn)行管理與調(diào)度。
Runnalbe接口定義了可執(zhí)行的任務(wù),它只有一個(gè)無(wú)返回值的run()函數(shù)。
線程的狀態(tài)
線程有四種狀態(tài),任何一個(gè)線程肯定處于這四種狀態(tài)中的一種:
- 產(chǎn)生(New):線程對(duì)象已經(jīng)產(chǎn)生,但尚未被啟動(dòng),所以無(wú)法執(zhí)行。
如通過(guò)new產(chǎn)生了一個(gè)線程對(duì)象后沒(méi)對(duì)它調(diào)用start()函數(shù)之前。 - 可執(zhí)行(Runnable):每個(gè)支持多線程的系統(tǒng)都有一個(gè)排程器,排程器會(huì)從線程池中選擇一個(gè)線程
并啟動(dòng)它。當(dāng)一個(gè)線程處于可執(zhí)行狀態(tài)時(shí),表示它可能正處于線程池中等待排排程器啟動(dòng)它;
也可能它已正在執(zhí)行。如執(zhí)行了一個(gè)線程對(duì)象的start()方法后,線程就處于可執(zhí)行狀態(tài),
但顯而易見(jiàn)的是此時(shí)線程不一定正在執(zhí)行中。 - 死亡(Dead):當(dāng)一個(gè)線程正常結(jié)束,它便處于死亡狀態(tài)。
如一個(gè)線程的run()函數(shù)執(zhí)行完畢后線程就進(jìn)入死亡狀態(tài)。 - 停滯(Blocked):當(dāng)一個(gè)線程處于停滯狀態(tài)時(shí),系統(tǒng)排程器就會(huì)忽略它,不對(duì)它進(jìn)行排程。
當(dāng)處于停滯狀態(tài)的線程重新回到可執(zhí)行狀態(tài)時(shí),它有可能重新執(zhí)行。如通過(guò)對(duì)一個(gè)線程調(diào)用wait()函數(shù)后,
線程就進(jìn)入停滯狀態(tài),只有當(dāng)兩次對(duì)該線程調(diào)用notify或notifyAll后它才能兩次回到可執(zhí)行狀態(tài)。
線程的wait、sleep、join和yield(面試必備)
-
wait()
讓當(dāng)前的線程等待,直到其他線程調(diào)用此對(duì)象的 notify()方法或 notifyAll()方法。
使當(dāng)前線程暫停執(zhí)行并釋放對(duì)象鎖標(biāo)示,讓其他線程可以進(jìn)入synchronized數(shù)據(jù)塊,
當(dāng)前線程被放入對(duì)象等待池中。當(dāng)調(diào)用notify()方法后,
將從對(duì)象的等待池中移走一個(gè)任意的線程并放到鎖標(biāo)志等待池中,
只有鎖標(biāo)志等待池中線程能夠獲取鎖標(biāo)志;如果鎖標(biāo)志等待池中沒(méi)有線程,則notify()不起作用。
notifyAll()則從對(duì)象等待池中移走所有等待那個(gè)對(duì)象的線程并放到鎖標(biāo)志等待池中。 -
sleep()
使當(dāng)前線程(即調(diào)用該方法的線程)暫停執(zhí)行一段時(shí)間,讓其他線程有機(jī)會(huì)繼續(xù)執(zhí)行,但它并不釋放對(duì)象鎖。也就是說(shuō)如果有synchronized同步快,其他線程仍然不能訪問(wèn)共享數(shù)據(jù)。注意該方法要捕捉異常。 -
join()
join()方法使當(dāng)前線程停下來(lái)等待,直至另一個(gè)調(diào)用join方法的線程終止。線程在被激活后不一定馬上就運(yùn)行,而是進(jìn)入到可運(yùn)行線程的隊(duì)列中。但是join()可以通過(guò)interrupt()方法打斷線程的暫停狀態(tài),從而使線程立刻拋出InterruptedException。 -
yield()
Yield()方法是停止當(dāng)前線程,讓同等優(yōu)先權(quán)的線程運(yùn)行。如果沒(méi)有同等優(yōu)先權(quán)的線程,那么Yield()方法將不會(huì)起作用。
線程方法 | 是否釋放同步鎖 | 是否需要在同步的代碼塊中調(diào)用 | 方法是否已廢棄 | 是否可以被中斷 |
---|---|---|---|---|
sleep() | 否 | 否 | 否 | 是 |
wait() | 是 | 是 | 否 | 是 |
suspend | 1.6廢棄 是 | |||
resume() | 1.6廢棄 是 | |||
join() | 否 | 是 |
擴(kuò)展:http://zheng12tian.iteye.com/blog/1233638
http://dylanxu.iteye.com/blog/1322066
關(guān)于wait和notify、notifyAll的運(yùn)用例子:
public static void main(String[] args) {
waitAndNotifyAll();
}
private static Object object = new Object();
private static void waitAndNotifyAll() {
System.out.println("主線程啟動(dòng)");
Thread thread = new WaitThread();
thread.start();
long startTime = System.currentTimeMillis();
try {
synchronized (object) {
System.out.println("主線程等待");
object.wait();
}
} catch (Exception e) {
// TODO: handle exception
}
long time = System.currentTimeMillis() - startTime;
System.out.println("主線程繼續(xù)->等待耗時(shí):" + time + " ms");
}
static class WaitThread extends Thread {
@Override
public void run() {
// TODO Auto-generated method stub
try {
synchronized (object) {
Thread.sleep(3000);
object.notifyAll();
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
運(yùn)行后的結(jié)果
主線程啟動(dòng)
主線程等待
主線程繼續(xù)->等待耗時(shí):3000 ms
與多線程相關(guān)的方法--Callable、Future和FutureTask
Runnable和Callable功能大致類似,不同的是Callable是一個(gè)泛型接口,切有一個(gè)返回值為Call()函數(shù),而Runnable的run()函數(shù)不能講結(jié)果返回給客戶程序。Callable的聲明:
public interface Callable<V>{
V call() throws Exception;
}
==可返回值的任務(wù)必須實(shí)現(xiàn)Callable接口,類似的,無(wú)返回值的任務(wù)必須Runnable接口==
Future提供了對(duì)Runnable或Callable任務(wù)的執(zhí)行結(jié)果進(jìn)行取消,查詢是否完成,獲取結(jié)果,設(shè)置結(jié)果操作,分別對(duì)應(yīng)cancel,isDone,get,set函數(shù)。get方法會(huì)阻塞,直到任務(wù)返回結(jié)果;
說(shuō)到底,F(xiàn)uture只是定義了一些規(guī)范的接口,而FutureTask才是具體實(shí)現(xiàn)類,F(xiàn)utureTask實(shí)現(xiàn)了RunnableFuture<V>,而RunnableFuture實(shí)現(xiàn)了Runnable有實(shí)現(xiàn)了Future<V>這2個(gè)接口,所以FutureTask具備了他們的能力
關(guān)于Runnable、Callable、FutureTask的運(yùn)用例子:
public static void main(String[] args) {
try {
futureWithRunnable();
futureWithCallable();
futureTask();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
static ExecutorService mExecutor = Executors.newSingleThreadExecutor();
private static void futureTask() throws InterruptedException,ExecutionException {
// TODO Auto-generated method stub
FutureTask<Integer> result = new FutureTask<Integer>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
// TODO Auto-generated method stub
return fibc(20);
}
});
mExecutor.submit(result);
System.out.println("FutureTask:"+result.get());
}
private static void futureWithCallable() throws InterruptedException,ExecutionException {
// TODO Auto-generated method stub
Future<Integer> result = mExecutor.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
// TODO Auto-generated method stub
return fibc(20);
}
});
System.out.println("Callable:"+result.get());
}
/**
* runnable無(wú)返回值,所以get()的值為null
*/
private static void futureWithRunnable() throws InterruptedException,ExecutionException {
//提交runnable,
Future<?> result = mExecutor.submit(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
fibc(20);
}
});
System.out.println("runnable:"+result.get());
}
//效率低夏的 斐波那契數(shù)列
private static int fibc(int i) {
// TODO Auto-generated method stub
if (i == 0) {
return 0;
}
if (i == 1) {
return 1;
}
return fibc(i-1) + fibc(i-2);
}
結(jié)果:
runnable:null
Callable:6765
FutureTask:6765
在上面的例子中,第一個(gè)結(jié)果為null是因?yàn)镽unnable沒(méi)有回調(diào)結(jié)果,所以get的值為null
第二個(gè)Callable那個(gè)是通過(guò)Future的get函數(shù)得到結(jié)果,
FutureTask則是一個(gè)RunnableFuture<V>,既實(shí)現(xiàn)了Runnable又實(shí)現(xiàn)了Future<V>,另外
還可以包裝Runnable(實(shí)際轉(zhuǎn)成Callable)和Callable<V>,提交給ExecuteService來(lái)執(zhí)行后也可以通過(guò)
返回的Future對(duì)象的get函數(shù)得到執(zhí)行結(jié)果,在線程體沒(méi)有執(zhí)行完成時(shí),主線程一直阻塞等待,執(zhí)行完則直接返回結(jié)果。
線程池
引子:當(dāng)需要頻繁創(chuàng)建多個(gè)線程進(jìn)行耗時(shí)操作時(shí),每次通過(guò)new Thread實(shí)現(xiàn)性能很差,缺乏統(tǒng)一管理,可能無(wú)限制新建線程導(dǎo)致線程之間的競(jìng)爭(zhēng),可能占用多系統(tǒng)資源導(dǎo)致死鎖,缺乏定時(shí)執(zhí)行,定期執(zhí)行,線程中斷等功能;
這時(shí)線程池就派上用場(chǎng)了,Java提供了4種線程池,它能有效管理調(diào)度線程,避免資源消耗,優(yōu)點(diǎn):
1,重用存在的線程,減少對(duì)象創(chuàng)建、銷毀的開(kāi)銷;
2,可有效控制最大并發(fā)線程數(shù)、提高系統(tǒng)資源的使用率,同事避免過(guò)多資源競(jìng)爭(zhēng),避免堵塞;
3,提供定時(shí)執(zhí)行、定期執(zhí)行、單線程、并發(fā)數(shù)控制等功能。
java中的線程池?cái)U(kuò)展:http://cuisuqiang.iteye.com/blog/2019372
==線程池都實(shí)現(xiàn)了ExecutorService接口,該接口定義了線程池需要實(shí)現(xiàn)的接口,如submit、execute、shutdown等==
啟動(dòng)指定數(shù)量的線程(ThreadPoolExecutor)與定時(shí)執(zhí)行任務(wù)(ScheduledThreadPoolExecutor)
1,ThreadPoolExecutor 是線程池的實(shí)現(xiàn)之一,功能是啟動(dòng)指定數(shù)量的線程以及將任務(wù)添加到一個(gè)隊(duì)列中,并且將任務(wù)分發(fā)給空閑的線程。
2,ScheduledThreadPoolExecutor在我們需要定時(shí)執(zhí)行一些任務(wù)的場(chǎng)景使用,通過(guò)Executors和newScheduledThreadPool函數(shù)就可方便地創(chuàng)建定時(shí)執(zhí)行任務(wù)的線程池。
擴(kuò)展:
ThreadPoolExecutor詳解 http://blog.chinaunix.net/uid-20577907-id-3519578.html
Java線程池使用說(shuō)明 http://www.oschina.net/question/565065_86540
JAVA線程池的分析和使用 http://www.infoq.com/cn/articles/java-threadPool/
Java 理論與實(shí)踐: 線程池與工作隊(duì)列 http://www.ibm.com/developerworks/cn/java/j-jtp0730/index.html
AysncTask的原理
AysncTask是解決Thread和Handler更新UI時(shí)的代碼臃腫,多任務(wù)無(wú)法精確控制等缺點(diǎn)而產(chǎn)生的,它的誕生使得創(chuàng)建異步任務(wù)變得更加簡(jiǎn)單,不在需要編寫(xiě)任務(wù)線程和Handler實(shí)例,相對(duì)Handler和Thread來(lái)說(shuō)易于使用
==使用注意:==
- 異步任務(wù)的實(shí)例必須在UI線程中創(chuàng)建
- execute(Params... params)方法必須在UI線程中調(diào)用
- 不能在doInBackground(Params... params)中更改UI組件的信息
- 一個(gè)任務(wù)實(shí)例只能執(zhí)行一次,如果執(zhí)行第二次將會(huì)拋出異常
- 不要在程序中直接調(diào)用 onPreExecute()、onPostExecute()、doInBackgroud 和 onProgressUpdate 方法;
AysncTask的執(zhí)行原理
- doInBackground(Params... params) 是一個(gè)抽象方法,繼承AsyncTask必須覆寫(xiě)此方法。
- onPreExecute()、onProgressUpdate(Progress... values)、onPostExecute(Result result)、onCancelled()這幾個(gè)方法體都是空的,需要的時(shí)候可以選擇性地覆寫(xiě)它們。
- publishProgress(Progress... values) 是final修飾的,不能覆寫(xiě),只能調(diào)用,一般都會(huì)在doInBackground(Params... params)中調(diào)用此方法來(lái)更新進(jìn)度條;
實(shí)現(xiàn)一個(gè)簡(jiǎn)單的AsyncTask
HandlerThread是自帶消息隊(duì)列的Thread類型,當(dāng)線程
public abstract class SimpleAsyncTask<Result> {
private static final HandlerThread ht = new HandlerThread("SimpleAsyncTask", Process.THREAD_PRIORITY_BACKGROUND);
static{
ht.start();
}
final Handler mUIHandler = new Handler(Looper.getMainLooper());
final Handler mAsyncHandler = new Handler(ht.getLooper());
protected void onPreExecute(){}
protected void onPostExecute(Result result){}
protected abstract Result doInBackground();
public final SimpleAsyncTask<Result> execute(){
onPreExecute();
mAsyncHandler.post(new Runnable() {
@Override
public void run() {
postResult(doInBackground());
}
});
return this;
}
private void postResult(final Result result){
mUIHandler.post(new Runnable() {
@Override
public void run() {
onPostExecute(result);
}
});
}
}
總結(jié):
多線程編程在應(yīng)用開(kāi)發(fā)中隨處可見(jiàn),網(wǎng)絡(luò)請(qǐng)求、IO操作等耗時(shí)操作都需要異步執(zhí)行,線程池是進(jìn)行異步操作的重要方式,在概念上十分簡(jiǎn)單,并且封裝良好,基本滿足需求。自行實(shí)現(xiàn)一個(gè)行為正確的線程池并不是那么容易,需要解決死鎖、資源不足、和wait()以及notify()等復(fù)雜問(wèn)題。
因此建議在Executor類族的基礎(chǔ)上正確的運(yùn)用而不建議自定義線程池。
參考書(shū)目:《Android從小工到專家》,《Android開(kāi)發(fā)藝術(shù)探索》