What
所謂生產(chǎn)者消費(fèi)者模式馏艾,就是一個(gè)地方無(wú)腦生產(chǎn)他宛,一個(gè)地方無(wú)腦消費(fèi)四濒,通過(guò)一個(gè)中間緩沖區(qū)建立的一種模式换况。這樣的解耦是不是很多人所向往的职辨,而解耦的關(guān)鍵是如何使用中間的緩沖區(qū)。生活中的例子也有很多戈二,像賣(mài)手機(jī)的舒裤,他們只負(fù)責(zé)生產(chǎn),而我們只負(fù)責(zé)消費(fèi)觉吭,中間的緩沖區(qū)便是他們的庫(kù)存腾供。再比如郵局,我們只負(fù)責(zé)寫(xiě)信鲜滩,收信人只負(fù)責(zé)收信伴鳖,中間的緩沖區(qū)便是郵局。還有徙硅,坐地鐵榜聂,上班打卡。嗓蘑。须肆。生活中處處充滿著這個(gè)模型。
有了生產(chǎn)和消費(fèi)桩皿,但是世界永遠(yuǎn)唯一不變的是變化豌汇,于是就產(chǎn)生了各種問(wèn)題,生產(chǎn)者和消費(fèi)者的量不一致泄隔,時(shí)間的把控瘤礁,效率的高低,都是問(wèn)題出現(xiàn)的因素梅尤。在美麗的大Android中很多地方也運(yùn)用到了這個(gè)模型,同樣的岩调,也會(huì)出現(xiàn)這個(gè)問(wèn)題巷燥,那么Android中是如何處理這些問(wèn)題的呢?他的緩沖區(qū)是如何做的呢号枕?
How
首先缰揪,看看Android中常用到這個(gè)模型的有哪些應(yīng)用?
曾經(jīng)面試的問(wèn)題葱淳,Android中有幾種方式可以在子線程中更新UI钝腺?
初學(xué)者看到這里,應(yīng)該會(huì)自豪的說(shuō):
1赞厕,runOnUiThread
2艳狐,view.post()
3,handler
前兩種方式的源碼 其內(nèi)部都實(shí)現(xiàn)了mHandler.post(action)方法皿桑,說(shuō)明這三種方式其實(shí)毫目,就是一種方式蔬啡,通過(guò)Handler機(jī)制實(shí)現(xiàn),關(guān)于Handler機(jī)制實(shí)現(xiàn)镀虐,請(qǐng)聽(tīng)下回分解箱蟆。
另外還有最熟悉的Toast
其內(nèi)部也是Handler:mHandler.obtainMessage(0, windowToken).sendToTarget();
Why
內(nèi)部的實(shí)現(xiàn)都是Hander機(jī)制,其實(shí)Android消息機(jī)制的核心便是Handler機(jī)制刮便,而實(shí)現(xiàn)消息機(jī)制模型就是生產(chǎn)者消費(fèi)者模型空猜。那么,Handler機(jī)制是如何實(shí)現(xiàn)的呢恨旱?
查看源碼一路追蹤辈毯,撥開(kāi)層層迷霧,可以在MessageQueue,Message中查看得到生產(chǎn)者消費(fèi)者模型的影子,Message就是生產(chǎn)出來(lái)的事物窖杀,而MessageQueue實(shí)現(xiàn)了生產(chǎn)和消費(fèi)操作功能漓摩。
MeesageQueue,具體查看代碼如下:
enqueueMessage()
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
next()
Message next() {
// Return here if the message loop has already quit and been disposed.
// This can happen if the application tries to restart a looper after quit
// which is not supported.
final long ptr = mPtr;
if (ptr == 0) {
return null;
}
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
// If first time idle, then get the number of idlers to run.
// Idle handles only run if the queue is empty or if the first message
// in the queue (possibly a barrier) is due to be handled in the future.
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
if (mPendingIdleHandlers == null) {
mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
}
mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
}
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
分析如下:
生產(chǎn)物
生產(chǎn)者:
enqueueMessage() 生產(chǎn)的對(duì)象為Message
if(beforeMessag==null||when=0||when<beforeMessag.when){
initMessage;
}else{//新消息,是入隊(duì)操作
prevMsg.next=curMsg;
}
Message p=Message mMessage;
Message prev;
loop //循環(huán)取出當(dāng)前鏈表最后一個(gè)message,賦值給prev;
->prev =p;
->p=p.next;
//賦值給Next
msg.next=p=null;
prev.next =msg;
消費(fèi)者:
next()
loop
->Message prevMsg=null; Message msg=mMessages;
//將下一個(gè)Msg上移入客,for loop 將剩下來(lái)的msg一一往前移動(dòng)
-> if(prevMsg!=null) prevMsg.next=msg.next;
-> else mMessages=msg.next;// 主鏈表上移一個(gè)msg
-> return msg;
1管毙,enqueueMessage() 為生產(chǎn)線程執(zhí)行,入隊(duì)一個(gè)Message ,return true桌硫。
2夭咬,next() 為消費(fèi)線程執(zhí)行,出隊(duì):在Looper.loop()中不斷取, 而在next()中也是loop 只要取到了便return msg 否則wait铆隘。next加了一個(gè)同步鎖,保證了與enqueue的互斥卓舵。enqueue 同樣也添加了同步鎖,從而保證了與next的互斥:將message添加到Message鏈表中去,判斷,如果出現(xiàn)阻塞了,需要進(jìn)行喚醒操作膀钠。妥妥的生產(chǎn)者消費(fèi)者模型掏湾。
總結(jié)
生產(chǎn)者和消費(fèi)者的精髓是:
不同線程操作同一對(duì)象的不同方法,但是要保持其互斥,也不能出現(xiàn)死鎖的情況,條件滿足就通知其他等待的線程 ,條件不滿足,就休眠等待。
在Thread-1的生產(chǎn)者只負(fù)責(zé)生產(chǎn),在Thread-2的消費(fèi)者則只負(fù)責(zé)消費(fèi),操作互斥,當(dāng)生產(chǎn)者達(dá)到上限則進(jìn)行等待,反之消費(fèi)者達(dá)到上限所有線程就等待肿嘲。
【引用】
1融击,模式解釋靈感:戳這里看大神的解釋
2,MessageQueue源碼解析
3雳窟,Toast源碼解析尊浪,艾瑪,和我看的順序一樣一樣的