Android線程學習索引

目錄

  • 線程狀態(tài)
  • 線程池
  • 線程安全
  • Java Memory Model
  • Volatile
  • Sychornized
  • ReentrantLock
  • 樂觀悲觀
  • 死鎖
  • jmm和jvm的區(qū)別
  • 線程通信handler機制
    • IntentService
    • ThreadLocal
  • 原子類(沒寫)
  • AsyncTask
  • 其他的同步
  • 進程
  • 參考鏈接

線程狀態(tài)

線程狀態(tài)轉換圖


new,runnable,running,dead,blocked,waiting/time waiting

狀態(tài)轉換的幾個方法及其比較

start()/run()的區(qū)別:

start是讓線程處于就緒狀態(tài)真正的實現(xiàn)多線程,內部是調用了run方法。而run方法不是洋丐,繼續(xù)同步調用吊圾。

其余幾個方法的比較:

yield—線程靜態(tài)方法,釋放鎖麻蹋,放棄這次cpu機會跛溉,下次再競爭,只給不比自己優(yōu)先級低的線程機會扮授,執(zhí)行后線程進入runnable狀態(tài)

sleep--靜態(tài)方法芳室,不釋放鎖,不考慮優(yōu)先級刹勃,執(zhí)行后進入block狀態(tài)堪侯,有的也說進入timewaiting狀態(tài),從不釋放鎖這點來看荔仁,我覺得是block狀態(tài)伍宦。

join--實例方法,使該實例進程阻塞乏梁,當被join的線程執(zhí)行完才執(zhí)行自己次洼,執(zhí)行進入wait/timewaiting狀態(tài)

wait—Object類實例方法,釋放鎖遇骑,必須放在同步塊里使用卖毁,自己則進入對象等待池,執(zhí)行后進入wait/timewaiting落萎,wait(time)就是進入timewaiting狀態(tài)亥啦,能提前notify么?

notify--實例方法练链,該實例對象移除對象等待池翔脱,進入鎖標志等待池

notifyAll--實例方法,喚醒所有的線程媒鼓,讓其競爭上崗


怎樣使用多線程

繼承thread届吁,實現(xiàn)runnable接口,實現(xiàn)callable接口


線程池

線程池ThreadPool的使用executorService.excute绿鸣,

根據(jù)其構造方法理解線程池

比如asyncTask的核心線程數(shù)Math.max(2,Math.min(CPU_COUNT,4))瓷产,至少2個,至多4個
最大線程數(shù) CPU_COUNT*2-1

okhttp的核心線程數(shù)0枚驻,最大數(shù)INT_MAX---為什么會這樣濒旦?后文有

優(yōu)化點就在減少線程的創(chuàng)建和銷毀時間,開發(fā)者更多的關注任務

構造方法的四個隊列及其特點

linkedBlockQueue再登,ArrayBlockQueue尔邓,DelayQueue晾剖,SynchronizedQueue

Okhttp的內部實現(xiàn)

可以借助OkHttpClient.newCall(request).equeue(Callback)來理解線程池的應用
參考okhttp章節(jié)的發(fā)送請求段落
http://www.reibang.com/p/6fa13048a6cf


線程安全問題

實質就是多線程的同步問題。
為什么會出現(xiàn)同步問題梯嗽,背景在數(shù)據(jù)讀寫這里齿尽,先看到讀寫方式(如圖)

image.png

1.程序運行過程中的臨時數(shù)據(jù)是存放在主存(物理內存)當中的;
cpu訪問速度和順序:register寄存器-->cpu cache mem(高速緩存)-->RAM 主存
2.由于CPU執(zhí)行速度很快灯节,而從內存讀取數(shù)據(jù)和向內存寫入數(shù)據(jù)的過程跟CPU執(zhí)行指令的速度比起來要慢的多循头;
3.如果任何時候對數(shù)據(jù)的操作都要通過和內存的交互來進行,會大大降低指令執(zhí)行的速度炎疆;
4.所以最終在CPU里面就有了高速緩存卡骂;
線程會從主存(堆區(qū))copy一份到棧內存里(高速緩存里),處理完后就把這個副本刷回去
5.但是問題在形入,有緩存不一致的問題全跨,也就是多線程訪問會訪問到緩存,而不是實時數(shù)據(jù)亿遂;
6.于是有了加鎖的概念(線程安全的問題)浓若。


Java Memory Model

解決線程安全模型,先來了解jmm

什么是Java內存模型蛇数?

https://mp.weixin.qq.com/s/_4AtyCVPj6E4AkHzVHsKbg

http://www.cnblogs.com/dolphin0520/p/3920373.html

因此并發(fā)可能遇到的三個問題
1.原子性:即一個操作或者多個操作 要么全部執(zhí)行并且執(zhí)行的過程不會被任何因素打斷挪钓,要么就都不執(zhí)行;
2.可見性:是指當多個線程訪問同一個變量時,一個線程修改了這個變量的值耳舅,其他線程能夠立即看得到修改的值;
3.有序性:即程序執(zhí)行的順序按照代碼的先后順序執(zhí)行碌上。

再更詳細的說
java內存模型的毛病
http://cmsblogs.com/?p=2161

實例化一個對象要分為三個步驟:

1.分配內存空間
2.初始化對象
3.將內存空間的地址賦值給對應的引用

但是由于重排序的緣故,步驟2挽放、3可能會發(fā)生重排序绍赛,其過程如下:
1.分配內存空間
2.將內存空間的地址賦值給對應的引用
3.初始化對象

如果2蔓纠、3發(fā)生了重排序就會導致第二個判斷會出錯辑畦,singleton != null,但是它其實僅僅只是一個地址而已腿倚,
此時對象還沒有被初始化纯出,所以return的singleton對象是一個沒有被初始化的對象

兩個解決辦法:

1.不允許初始化階段步驟2 、3發(fā)生重排序敷燎。
2.允許初始化階段步驟2 暂筝、3發(fā)生重排序,但是不允許其他線程“看到”這個重排序硬贯。


Volatile

volatile關鍵字焕襟,用來修飾變量
volatile在以下三方面影響java內存模型,從而保證線程安全

1.原子性:Java中對基本數(shù)據(jù)類型的變量的讀取和賦值操作是原子性操作饭豹,即這些操作是不可被中斷的鸵赖,要么執(zhí)行务漩,要么不執(zhí)行,其余都是非原子操作;
*volatile不保證原子性操作,所以對原子性沒幫助

2.可見性:Java里的一個共享變量被volatile修飾時它褪,它會保證修改的值會立即被更新到主存饵骨,當有其他線程需要讀取時,它會去內存中讀取新值茫打;
*volatile保證修改的值會立即被更新到主存

3.有序性:JavaJ內存模型中居触,允許編譯器和處理器對指令進行重排序,但是重排序過程不會影響到單線程程序的執(zhí)行老赤,卻會影響到多線程并發(fā)執(zhí)行的正確性轮洋;
*volatile原則屬于 happens-before原則的一部分,能部分保證有序性

從匯編角度了解volatile禁止重排序诗越,了解happens-before原則
http://cmsblogs.com/?p=2148


Synchronize

Synchronize關鍵字砖瞧,本身不是鎖,只是去拿鎖

從jmm角度分析synchronize和volatile的區(qū)別
1.原子性
代表了原子性的操作(幾個步驟是一個原子)在線程執(zhí)行過程中不會被中斷嚷狞,是一個整體块促;
原子性是拒絕多線程操作的,不論是多核還是單核床未,具有原子性的量竭翠,同一時刻只能有一個線程來對它進行操作,所以synchronize可以保證變量原子性薇搁;
volatile能保證變量在私有內存和主內存間的同步斋扰,但是多個線程操作仍然可能打斷,所以不能保證變量的原子性啃洋;
2.可見性
volatile是變量在多線程之間的可見性(使得緩存數(shù)據(jù)無效)传货,synchronize并不會馬上更新到主存里,只不過只有一個線程操作,數(shù)據(jù)繼續(xù)是緩存->主存宏娄;
3.有序性
都是一定程度保證有序性问裕,volatile的有序性規(guī)則不細說,synchronize是保證同步代碼塊內的代碼不會重排序到同步塊之外(其實也是monitor屏障)
4.其他方面
volatile是線程同步的輕量級實現(xiàn)孵坚,多線程訪問volatile不會發(fā)生阻塞粮宛,而synchronize會發(fā)生阻塞,所以volatile的性能要比synchronize好卖宠;
volatile只能用于修飾變量巍杈,synchronize可以用于修飾方法、代碼塊扛伍,對于多線程訪問同一個實例變量還是需要加鎖同步筷畦。

鎖范圍

Java并發(fā)之synchronized深度解析
https://mp.weixin.qq.com/s/xPUKTIIwz4sU1cK1eWy21w

鎖思想
http://www.reibang.com/p/94cf9ebd8932
包含,悲觀(就是鎖上)樂觀(認為不會修改刺洒,寫少的場景鳖宾,無鎖亚斋,CAS操作)。
鎖升級: 自旋鎖(先不釋放鎖攘滩,下次申請鎖還是自己就不用再加鎖消耗)帅刊,輕量鎖(等待,里面有個id漂问,判斷到?jīng)]到自己)赖瞒,重量鎖(直接掛起等待喚醒)

結合單例DCL去講

餓漢法寫一個單例
https://blog.csdn.net/Jo__yang/article/details/52117031

https://mp.weixin.qq.com/s/N6UqsoWLEUWFFu2S_OT75w

那些年,我們一起寫的單例模式
https://mp.weixin.qq.com/s/pixuEDQ_OZ0RFjciTThKlQ

那為什么用enum寫單例是線程安全的蚤假?
http://www.hollischuang.com/archives/197


各種鎖示意圖

圖源https://mp.weixin.qq.com/s/tQ4dgerk9-FR4HIW5rwHpQ

ReentrantLock

reentrantLock重入鎖栏饮,—這個沒咋看,不常用
結合DelayQueue的take方法
https://blog.csdn.net/kobejayandy/article/details/46833623


樂觀悲觀

鎖繼續(xù)發(fā)散就是樂觀鎖和悲觀鎖
這兩種不是真正的鎖磷仰,而是一種思想袍嬉,前面說的加鎖都是悲觀鎖,cas是一種樂觀鎖的實現(xiàn)形式灶平。
悲觀鎖就是每個操作前都鎖定伺通,樂觀鎖就是先讀取操作后再比較,適用于讀高發(fā)的場景逢享。具體參考
CAS (compareAndSwap)
https://www.cnblogs.com/qjjazry/p/6581568.html


死鎖

死鎖的概念
代碼實現(xiàn)一個死鎖罐监,如何解決死鎖?
http://cmsblogs.com/?p=1312


jvm 跟jmm的區(qū)別

jvm是指java虛擬機的內部結構模型瞒爬,內部包括: 線程共享方法區(qū)&堆區(qū)弓柱,線程獨享棧區(qū),本地方法區(qū)侧但,程序計數(shù)器矢空。

數(shù)據(jù)結構的線程安全問題

有時候是從數(shù)據(jù)結構深入到線程安全。
比如先問HashMap和LinkedHashMap的區(qū)別禀横,然后問HashMap的缺點屁药,
再對比HashMap和HashTable,Collection.SynchornizedMap的優(yōu)缺燕侠,提出改進方案用ConcurrentHashMap--->結構特點

這里再發(fā)散一下
有點像ArrayList和LinkedList比較者祖,再比較Vector立莉,和CopyOnWriteArrayList


多線程中的通信問題

就是考Handler的消息機制

首先handler收發(fā)消息的圖要會畫

然后通過各自的構造方法去梳理一下

handler/thread/threadLocal/looper/messageQueue 的對應關系

以activity在主線程啟動的為例(handler有關的)

1.ActivityThread的main方法里Looper.prepareMainLooper(就是prepare方法)绢彤;
2.activityThread就綁定了這個線程,雖然我不知道跟這個handler有啥用蜓耻;
3.并且主動調用loop循環(huán)取消息茫舶;

prepareLooper方法做了兩件事

1.判斷當前線程的threadLocal里有沒有l(wèi)ooper,有就報錯刹淌,因為一個線程里只能有一個looper饶氏;
2.threadLocal(looper類的餓漢初始化)里沒有l(wèi)ooper讥耗,則初始化looper并set進去;
這里細節(jié)
Looper prepare的時候疹启,
先檢查 threadLocal古程,先get,看能不能拿到looper
再set喊崖,把looper set到threadLocal里
ThreadLocal 的get方法挣磨,很詭異
看起來是key,value荤懂,key是thread茁裙,value是looper

實際上是獲取 thread里私有的threadLocal,
因為ThreadLocal.ThreadLocalMap是線程私有的节仿。
這個ThreadLocalMap是ThreadLocal的靜態(tài)內部類晤锥。

然后并不是真正的map。而是一個 Entry數(shù)組廊宪。
這個數(shù)組存的方式也很奇怪矾瘾,index是threadLocal的hashCode
value是Entry。
entry的key是 ThreadLocal箭启,value是其持有的私有對象值霜威,這里是looper

然后,這個entry册烈,繼承的是一個WeakReference<ThreadLocal<?>>
這個能保證戈泼,weakReference里的threadLocal 就不算強引用,可以正成蜕回收大猛,threadlocalMap也就可以回收,這樣里面的一些私有變量就可以被回收淀零。

舉個例子挽绩, userInfo放到threadLocal里,做線程私有變量驾中,當userInfo不用了唉堪,userInfo==null,
但是threadLocal還持有userInfo(集合持有)肩民,threadLocal生命周期跟thread一起唠亚,可能這個thread就一直沒釋放,也就沒回收持痰,導致內部的threadLocalMap就一直保留著userInfo.

這里就可以做到灶搜,threadLocal的remove函數(shù),會主動清理掉entry,避免集合引用導致的泄露割卖。

這里可以看這篇文章
https://mp.weixin.qq.com/s/8Emvbmbn1CgBPjddETEjMA

Looper的初始化方法里做了兩件事

1.綁定一個messageQueue前酿,所以一個looper只有一個msgQueue;
2.綁定當前所處線程鹏溯;
所以主線程默認是有一個looper罢维,一個msgQueue,主線程的handler是不需要額外prepare的

然后就是1個線程可以有多個handler(實際就是1個looper可以有多個handler)參考主線程丙挽,其余都是一一擁有關系言津。

收發(fā)消息

handler.sendMsg/obtainMsg的區(qū)別

handler怎樣做到MsgDelay

->handler.sendMsgDelay
->msg內部存儲了一個時間戳
->sendMessageAtTime方法綁定一個msgQueue(也從側面說明,一個handler只能同一時間處理一個msgQueue的消息)
->handler.enqueueMessage
->msgQueue.enqueueMessage
->內部有個死循環(huán)來判斷排隊

這里經(jīng)常會被問到為什么msg delay不會被阻塞取试,比如sendMsgDelay 悬槽,第一個msg delay 30s,第二個msg delay 5s
那么第二個msg真的會在第35s才被接收嗎瞬浓?
這里涉及到的就是msg的阻塞問題初婆,

實際上結合MessageQueue的enquene方法,和msg的next方法猿棉,內部維護一個needAwake
首先判斷的是消息是否退出磅叛,如果要退出就回收消息,然后獲取到正在使用的消息并將傳遞過來的時間(即延時時間)賦值給msg.when萨赁。而p又是什么弊琴?其實p = mMessages,第一次進來p是null杖爽,接著判斷消息是否為null敲董,當前消息的when是否為0或者小于p.when滿足這個條件,p 就是下一個要執(zhí)行的消息即(msg.next),再給needWake賦值時刻喚醒消息慰安。如果不滿足上面的條件(即線程阻塞了)則開啟一個死循環(huán)將其插入到隊列中腋寨,讓when = 0的消息或者when < p.when(即延時小的消息)先執(zhí)行。

loop方法的內部實現(xiàn)

內部一個死循環(huán)不斷去取msg化焕,取到之后msg.target.dispatchMsg萄窜,這個msg.target本質是handler對象,
在handler.obtain的時候撒桨,msg就綁定上了一個target查刻,handler.send的是在equeueMsg的時候綁上的,最后由這個handler去handleMsg

這里會被問到凤类,為什么這個loop的死循環(huán)不會阻塞UI線程穗泵?
在MessageQueue.next()方法里,會調用一個native方法:nativePollOnce(long ptr, int timeoutMillis)踱蠢,當主線程沒有消息可處理的時候火欧,該方法會阻塞主線程。在這種情況下茎截,用戶點擊一下屏幕
nativePollOnce()方法繼續(xù)執(zhí)行了苇侵,并且調用了InputEventReceiver.dispatchInputEvent()
調用nativePollOnce()方法掛起主線程之后,當有一些事件到來時企锌,native層會喚醒主線程
https://www.zhihu.com/question/34652589

IntentService/HandlerThread的學習與使用

http://www.reibang.com/p/b9427d4011e0

ThreadLocal

面試必問:Android Handler機制之ThreadLocal
https://mp.weixin.qq.com/s/WOYUWPSSrGIaX1uuboRrqg

ThreadLocal解析
https://mp.weixin.qq.com/s/VNBwPPJeENyU9iyxJcN1qw


AsyncTask

使用
http://www.reibang.com/p/7d2fa022b647

源碼
http://www.reibang.com/p/97da1c0f21c4

強烈推薦看這個
關于創(chuàng)建ayncTask的
https://mp.weixin.qq.com/s/5CeZ6NHF6dm3qN6RgzaGDQ


其他的同步

Condition 與傳統(tǒng)多線程協(xié)作區(qū)別問題解析
https://mp.weixin.qq.com/s/naUaxbcenTZeiQ441wyQbA

Android Exchanger
https://mp.weixin.qq.com/s/bOFsVALa8oBbhvcrzCIbYw


進程

Android技能樹 — 多進程相關小結
https://mp.weixin.qq.com/s/DgMXCqRh5sQiIDEUeTPzcw


參考鏈接

android 多線程 — 綜述
http://www.reibang.com/p/52d752dac4aa

還有一些線程的問題
https://mp.weixin.qq.com/s/QQt8sPOjsqQ0kHtfGsSlMg

JAVA多線程和并發(fā)基礎面試問答
http://blog.jobbole.com/76308/

Java線程面試題 Top 50
http://www.importnew.com/12773.html

40個Java多線程問題總結
http://www.cnblogs.com/xrq730/p/5060921.html

handler實現(xiàn)原理榆浓,從源碼分析
http://www.2cto.com/kf/201605/507567.html

多線程的實際應用例子
簡易斷點續(xù)傳下載器實現(xiàn)
http://www.reibang.com/p/5b2e22c42467

Android多線程斷點續(xù)傳下載
http://www.reibang.com/p/2b82db0a5181

Android里怎么回主線程操作
https://www.cnblogs.com/jingmo0319/p/5730963.html

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市撕攒,隨后出現(xiàn)的幾起案子陡鹃,更是在濱河造成了極大的恐慌,老刑警劉巖抖坪,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件萍鲸,死亡現(xiàn)場離奇詭異,居然都是意外死亡擦俐,警方通過查閱死者的電腦和手機脊阴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蚯瞧,“玉大人嘿期,你說我怎么就攤上這事÷窈希” “怎么了备徐?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長甚颂。 經(jīng)常有香客問我蜜猾,道長,這世上最難降的妖魔是什么振诬? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任瓣铣,我火速辦了婚禮,結果婚禮上贷揽,老公的妹妹穿的比我還像新娘棠笑。我一直安慰自己,他們只是感情好禽绪,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布蓖救。 她就那樣靜靜地躺著,像睡著了一般印屁。 火紅的嫁衣襯著肌膚如雪循捺。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天雄人,我揣著相機與錄音从橘,去河邊找鬼念赶。 笑死,一個胖子當著我的面吹牛恰力,可吹牛的內容都是我干的叉谜。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼踩萎,長吁一口氣:“原來是場噩夢啊……” “哼停局!你這毒婦竟也來了?” 一聲冷哼從身側響起香府,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤董栽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后企孩,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體锭碳,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡呵恢,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年婶芭,在試婚紗的時候發(fā)現(xiàn)自己被綠了尤泽。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片漓骚。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡妖爷,死狀恐怖竟秫,靈堂內的尸體忽然破棺而出晃虫,到底是詐尸還是另有隱情迄汛,我是刑警寧澤癣丧,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布槽畔,位于F島的核電站,受9級特大地震影響胁编,放射性物質發(fā)生泄漏厢钧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一嬉橙、第九天 我趴在偏房一處隱蔽的房頂上張望早直。 院中可真熱鬧,春花似錦市框、人聲如沸霞扬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽喻圃。三九已至,卻和暖如春粪滤,著一層夾襖步出監(jiān)牢的瞬間斧拍,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工杖小, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留肆汹,地道東北人愚墓。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓,卻偏偏與公主長得像昂勉,于是被迫代替她去往敵國和親浪册。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內容