Android的併發編程,即多線程開發,而Android的多線程開發模型也是源於Java中的多線程模型。
因此本篇也會先講一些Java中的多線程理念,再講解具體涉及的類,最後深刻Android中的併發場景和實踐。java
舉個很簡單的栗子,當你一邊在擼擼擼,一邊在看小視頻,同時在作兩件事,這就是併發。編程
咳,年輕人節制啊。安全
當一個任務並無徹底佔用系統資源,就能夠利用併發來提升資源利用率,同時也能更快地完成任務。多線程
當你的右手在幹些什麼的時候,左手是否是在沒事作呢?那就也用起來唄。
(某次聚會,一名騎馬的漢子說本身左右互搏(lu)特厲害)。併發
就拿上一個栗子來講,左手作什麼,右手作什麼,任務明確分配好,又能同時進行,既提升了效率,邏輯又清晰。app
這個拿Android客戶端舉個栗子,上傳圖片時,當前界面仍是正常運轉沒有卡死,圖片也正常上傳,既保證了界面被響應,又保證圖片能夠上傳。框架
就拿Android中的App來講,通常來講一個app就是一個進程,(除了特殊的手段開啓了多個進程,這裏不深刻這個話題,就是一個一對多的關係)。性能
進程只是一個程序、任務的統稱,可是卻不能執行任務,真正執行任務的是線程,因此線程是由進程建立的,一個進程能夠建立多個線程。學習
線程能夠調度資源等等,在這裏只須要了解大體的概念就好,若是要深刻能夠學習一下操做系統。操作系統
人腦就至關因而CPU,想作一件事的時候,這個任務就是一個進程了,須要運用手腳等器官去完成這個任務,而手腳器官就能夠理解成一個個線程,去作了不一樣的事,從而完成任務。
仍是用Android舉栗子,當你在手機上操做的時候,這個被稱之爲UI線程(以後會詳解)。而一個最基本的app,不須要複雜的功能時,就只有一個UI線程和咱們交互,那麼這個app就是個單線程的。通常的程序面向用戶的線程就是UI線程,也稱之爲主線程,單線程程序,其實就是隻有一個主線程的程序。
多個進程能夠算是併發,可是咱們所說的併發場景,大部分是在一個進程中的,而併發就是由線程完成的,多個線程同時執行任務,就稱之爲併發。
如下爲多線程工做示意圖:
A線程要寫文件C,B線程也要寫文件C,這個時候就好像你拿着兩隻筆同時往紙上寫東西,寫出來的是什麼本身也不知道。
這個時候咱們須要一個相似於鎖的東西,當C被A在寫的時候,B不能寫,B要等A寫完了才能繼續寫。
至於這個鎖究竟是什麼會在後面繼續講到。
死鎖的四個條件是:
禁止搶佔:no preemption
持有和等待:hold and wait
互斥:mutual exclusion
循環等待:circular waiting
預防死鎖就是至少破壞這四個條件其中一項,即破壞「禁止搶佔」、破壞「持有等待」、破壞「資源互斥」和破壞「循環等待」。
舉個例子:
A在B那邊割包皮,B把A割壞了,A佔着B的牀位,要B賠錢,B要A讓出牀位纔給錢。雙方僵持不下。
在Java中,線程一般就是指Thread
這個類,或者實現了Runnable
的類,其實Thread
這個類也是實現了Runnable
接口的,能夠看一下Runnable
接口的代碼:
裏面就是一個run方法須要被實現。
再看一下Thread
類的聲明:
確實是一個實現了Runnable
的類。
那麼Thread類中擁有start()
方法,和run()
方法,下面用run()
方法直接調用
獲得信息:
發現其實和外面的線程是在同一個線程上。
而調用start()
方法獲得的信息是:
發現線程名不同了,用start會開啓一個新的線程,而run仍是在當前線程執行。
另外在Java1.5以後,還有Callable、Future和FutureTask,在這裏就不詳細介紹,還有線程的wait、
yield、sleep等在下一章會一塊兒詳細介紹。
在Java中,線程的優先級有1~10,而默認的是5。1最低,10最高。在Thread類中有三個常量:
MIN_PRIORITY = 1
NORM_PRIORITY = 5
MAX_PRIORITY = 10
在同一個線程池中的線程優先級是相同的。
JVM會根據線程的優先級去搶先調度,然而線程的優先級只能保證搶佔資源的機率較大,並不能保障線程的執行順序,因此不能過於依賴設置線程的優先級。
頻繁地建立和銷燬線程會致使性能大幅度下降,這確定不是你但願的。
線程池的出現,就是爲了解決這個問題,根據java中提供不一樣的線程池機制,有效地提升資源利用率。
直接在代碼中建立Thread、Runnable去start或者run容易出現不可預測的問題,在java1.5開始,引入了java.util.concurrent包,其中有個併發的框架:Executor
,使用ExecutorService
替代直接操做線程類,而Executors
是用來建立線程池的,內部提供了不少靜態方法去建立你想要的線程池,不須要你再手動去建立實現。
看一下關於Executor中的類和接口的大體的成員與關係:
關於這些類如何使用,以及有什麼特性,下一章會做介紹。
在java中提到隊列確定會想起Queue
,而線程隊列用的是BlockingQueue
,這是個接口,在concurrent
包中有好幾個類實現了這個接口。
介紹一下BlockingQueue經常使用的方法
會異常 | 返回是否成功 | 會阻塞 | 設定等待時間 | |
---|---|---|---|---|
入隊列 | add(e) | offer(e) | put(e) | offer(e, timeout, unit) |
出隊列 | remove() | poll() | take() | poll(time, unit) |
查看值 | element() | peek() | none | none |
在前面講過死鎖,死鎖是因爲使用不當引發的一種現象,而這裏的鎖是人工干預的,讓併發按照你的意思走。
在java中的鎖有synchonrized、Lock。鎖的出現主要是爲了解決線程安全問題。
關於線程的狀態會在下一章講鎖的機制時候再講,由於線程的狀態會影響到鎖。
由於多線程訪問資源可能會形成數據不一致或者數據污染,而某些集合會用一些鎖或者同步機制作了處理。
線程安全的集合有:HashTable、SynchronizedCollection、ConcurrentHashMap、Vector等。
線程安不安全的首要前提是在多線程訪問同一個對象的狀況下。