Android知識架構 · 電話面試 · Android執行緒和程序以及安全問題
這篇文章介紹的幾個問題:
一 、程序和執行緒,以及區別
程序(Process):當一個程式進入記憶體執行時,即變成一個程序。程序是處於執行過程中的程式,是程式的一個執行例項。 程序是作業系統進行資源分配和排程的一個獨立單位。
執行緒(Thread):執行緒是作業系統能夠進行運算排程的最小單位,它被包含在程序之中,是程序中的實際運作單位。
併發和並行
併發:是指在同一時間點只能有一條指令執行,但多個程序指令被快速輪換執行,使得在巨集觀上具有多個程序同時執行的效果。
並行:指在同一時間點,有多條指令在多個處理器上同時執行。
·
程序和執行緒的區別
執行緒是程序的組成部分,一個程序可以有很多執行緒,每條執行緒並行執行不同的任務。
不同的程序使用不同的記憶體空間,而執行緒與父程序的其他執行緒共享父程序的所擁有的全部資源。這樣程式設計方便了,但是要更加小心。
別把記憶體空間和棧記憶體搞混,每個執行緒都擁有單獨的棧記憶體用來儲存本地資料。執行緒擁有自己的堆疊、自己的程式計數器和自己的區域性變數,但不擁有系統資源。
執行緒的排程和管理由程序本身負責完成。作業系統對程序進行排程,管理和資源分配。
二 、Android中的程序
Android存在五種級別的程序:前臺程序、可見程序、服務程序、後臺程序、空程序。
- 前臺程序(foreground process):前臺程序是使用者當前正在使用的程序。
存在前臺程序的條件:
- 程序中的某個Activity正在與使用者進行互動(Activity的onResume()方法被呼叫);
- 繫結到與當前使用者正在互動的activity的Service所在的程序;
- 程序中的某個Service正執行在前臺,即這個service的startForeground()方法被呼叫;
- 程序中的某個Service正在執行生命週期回撥方法(比如,onCreate(),onStart(),或者onDeatroy());
- 程序中的BroadcastReceiver正在執行onReceive()方法;
- 可見程序(visible process):可視程序是指沒有前臺執行的元件,但仍然會對使用者在螢幕看到的內容造成影響的程序。
存在可見程序的條件:
- 程序執行的Activity不在前臺,但仍然是可見的(呼叫了onPause()方法)。這種情況可能是這樣的,正在前臺執行的Activity啟動了一個對話方塊,這個對話方塊懸浮在這個activity之上,但仍有部分可見;
- 程序中的Service繫結到了一個可視(或前臺)的activity(該activity已呼叫了onPause()方法);
- 服務程序(service process):執行著一個通過startService() 方法啟動的service,這個service不屬於上面提到的2種更高重要性的。
service所在的程序雖然對使用者不是直接可見的,但是他們執行了使用者非常關注的任務(比如播放mp3,從網路下載資料)。只要前臺程序和可見程序有足夠的記憶體,系統不會回收他們。
- 後臺程序(background process):後臺程序是指程序中的activity當前對使用者來說不可見(這個activity呼叫了onStop()方法)。
後臺程序不會對使用者的體驗造成任何影響,並且系統可以在前臺程序、可視程序、服務繼承需要記憶體資源的時候會殺死後臺程序。通常會有很多後臺程序執行,並且這些後臺程序儲存在一個最近使用列表中,這樣做的好處就是保證使用者最近看到的程序最後被殺死。如果一個activity已經正確的實現了生命週期方法,並且儲存了當前的狀態,那麼系統殺死這些後臺程序對使用者的可視效果來說的話,沒有任何影響,因為當用戶返回回來的時候,這個activity已經儲存了所有的可視狀態。
- 空程序(empty process):一個空程序沒有任何執行的程式元件。
系統保持空程序存在的唯一原因就是為了快取方面的考慮,這樣做主要是為了提高元件的啟動時間。系統經常會殺死這些空程序來保持整個系統資源和核心快取之間的平衡。
因為一個正在執行的服務所在的程序的重要性高於一個處於後臺的activity所在的程序,所以根據這一點,如果一個activity如果要執行需要長時間執行的操作的話,這個activity最好為該操作啟動一個新的服務,而不是僅僅建立一個工作執行緒,尤其是當這個工作執行緒執行的時間可能比該activity的執行時間還長的時候。
三 、多執行緒
為什麼Android是執行緒不安全的?
Android單執行緒模型的兩個原則:不能阻塞UI執行緒;不要再UI執行緒外訪問Android UI toolkit。
也就是說,在非UI執行緒操作UI 和 在UI執行緒進行耗時操作都會出現錯誤。
既然耗時操作不能再UI執行緒中進行,我們當然會想到新開執行緒處理耗時操作,這就會出現多執行緒的問題。同時,耗時操作在單獨的程序中完成後,得到的結果必須要在UI執行緒展示,就會涉及程序間通訊的問題。
Java建立執行緒的方法:
- 繼承Thread類建立執行緒類
1、定義Thread類的子類,重寫該類的run()方法。該方法為執行緒執行體;
2、建立Thread子類的例項。即執行緒物件;
3、呼叫執行緒物件的start()方法啟動該執行緒;
- 實現Runnable介面建立執行緒類
1、定義Runnable介面的實現類,重寫該介面的run()方法。該方法為執行緒執行體;
2、建立Runnable實現類的例項。並以此例項作為Thread的target來建立Thread物件。該Thread物件才是真正的執行緒物件;
3、呼叫執行緒物件(該Thread物件)的start()方法啟動該執行緒;
Android中多執行緒的幾種工具類:
AsyncTask
HandlerThread
ThreadPool
IntentService
四 、執行緒同步
執行緒同步:當使用多個執行緒來訪問同一個資料時,非常容易出現執行緒安全問題(比如多個執行緒都在操作同一資料導致資料不一致),所以我們用同步機制來解決這些問題。執行緒同步的目的就是避免執行緒“同步”執行。
同步機制既可以同步程式碼塊、又可以同步方法:
//同步程式碼塊
synchronized (obj) {
...
}
//同步方法
public synchronized void methodName() {
....
}
當使用synchronized 來修飾某個共享資源時,當某個執行緒獲得共享資源的鎖後就可以執行相應的程式碼段,直到該執行緒執行完該程式碼段後才釋放對該共享資源的鎖,讓其他執行緒有機會執行對該共享資源的修改。當某個執行緒佔有某個共享資源的鎖時,如果另外一個執行緒也想獲得這把鎖執行就需要使用wait() 和notify()/notifyAll()方法來進行執行緒通訊了。
執行緒同步的特徵
1、 如果一個同步程式碼塊和非同步程式碼塊同時操作共享資源,仍然會造成對共享資源的競爭。因為當一個執行緒執行一個物件的同步程式碼塊時,其他的執行緒仍然可以執行物件的非同步程式碼塊。(所謂的執行緒之間保持同步,是指不同的執行緒在執行同一個物件的同步程式碼塊時,因為要獲得物件的同步鎖而互相牽制)
2、 每個物件都有唯一的同步鎖
3、 在靜態方法前面可以使用synchronized修飾符。
4、 當一個執行緒開始執行同步程式碼塊時,並不意味著必須以不間斷的方式執行,進入同步程式碼塊的執行緒可以執行Thread.sleep()或執行Thread.yield()方法,此時它並不釋放物件鎖,只是把執行的機會讓給其他的執行緒。
5、 Synchronized宣告不會被繼承,如果一個用synchronized修飾的方法被子類覆蓋,那麼子類中這個方法不在保持同步,除非用synchronized修飾。
死鎖
執行緒1獨佔(鎖定)資源A,等待獲得資源B後,才能繼續執行;
執行緒2獨佔(鎖定)資源B,等待獲得資源A後,才能繼續執行;
這樣就會發生死鎖,程式無法正常執行。
為避免死鎖,當幾個執行緒都要訪問共享資源A、B、C 時,保證每個執行緒都按照同樣的順序去訪問他們。
lock和synchronized:
摘自:百度知道
總的來說,lock更加靈活。主要相同點:Lock能完成synchronized所實現的所有功能
不同:
1.ReentrantLock功能性方面更全面,比如時間鎖等候,可中斷鎖等候,鎖投票等,因此更有擴充套件性。在多個條件變數和高度競爭鎖的地方,用ReentrantLock更合適,ReentrantLock還提供了Condition,對執行緒的等待和喚醒等操作更加靈活,一個ReentrantLock可以有多個Condition例項,所以更有擴充套件性。
2.ReentrantLock必須在finally中釋放鎖,否則後果很嚴重,編碼角度來說使用synchronized更加簡單,不容易遺漏或者出錯。
3.ReentrantLock 的效能比synchronized會好點。
4.ReentrantLock提供了可輪詢的鎖請求,他可以嘗試的去取得鎖,如果取得成功則繼續處理,取得不成功,可以等下次執行的時候處理,所以不容易產生死鎖,而synchronized則一旦進入鎖請求要麼成功,要麼一直阻塞,所以更容易產生死鎖。1、ReentrantLock 擁有Synchronized相同的併發性和記憶體語義,此外還多了 鎖投票,定時鎖等候和中斷鎖等候
執行緒A和B都要獲取物件O的鎖定,假設A獲取了物件O鎖,B將等待A釋放對O的鎖定,
如果使用 synchronized ,如果A不釋放,B將一直等下去,不能被中斷
如果 使用ReentrantLock,如果A不釋放,可以使B在等待了足夠長的時間以後,中斷等待,而幹別的事情
ReentrantLock獲取鎖定與三種方式:
a) lock(), 如果獲取了鎖立即返回,如果別的執行緒持有鎖,當前執行緒則一直處於休眠狀態,直到獲取鎖b) tryLock(), 如果獲取了鎖立即返回true,如果別的執行緒正持有鎖,立即返回false;
c) tryLock (long timeout, TimeUnit unit), 如果獲取了鎖定立即返回true,如果別的執行緒正持有鎖,會等待引數給定的時間,在等待的過程中,如果獲取了鎖定,就返回true,如果等待超時,返回false;
d) lockInterruptibly:如果獲取了鎖定立即返回,如果沒有獲取鎖定,當前執行緒處於休眠狀態,直到或者鎖定,或者當前執行緒被別的執行緒中斷
2、synchronized是在JVM層面上實現的,不但可以通過一些監控工具監控synchronized的鎖定,而且在程式碼執行時出現異常,JVM會自動釋放鎖定,但是使用Lock則不行,lock是通過程式碼實現的,要保證鎖定一定會被釋放,就必須將 unLock()放到finally{} 中
3、在資源競爭不是很激烈的情況下,Synchronized的效能要優於ReetrantLock,但是在資源競爭很激烈的情況下,Synchronized的效能會下降幾十倍,但是ReetrantLock的效能能維持常態;
五 、程序間通訊
程序間通訊:Inter-process communication (IPC)是指執行在不同程序(無論是否是同一臺機器)中的若干執行緒間的資料交換。
程序間通訊的兩種方法:檔案共享、作業系統提供的公共資訊機制。
計算機程序間通訊的幾種機制:
- 共享記憶體(Shared Memory)
通過訪問公共記憶體空間。
- 管道(Pipe)
管道也是作業系統中常見的一種程序間通訊方式,管道是單向的,如果同時有讀和寫的操作,就需要建立兩根管道。
- UDS(Unix Domain Socket)
Socket在網路通訊領域獲得了廣泛的應用,被稱為Network Socket,對於同一臺機器的程序間通訊,他也完全能夠勝任。UDS是專門針對單機內的程序間通訊提出來的,有時也被成為IPC Socket。
Android中使用最多的一種IPC機制是Binder,其次就是UDS。
- RPC (Remote Procedure Calls)
RPC設計的通訊雙方通常執行與兩臺不同的機器中。
六 、Handler、MessageQuere、Runnable、Looper
message
message是訊息內容;
messageQuere
messageQuere是訊息佇列,裡面存很多的message;
handler
訊息的真正處理者;
通知MQ它要執行一個任務(sendMessage),並在loop到自己的時候執行該任務(handleMessage),整個過程是非同步的。
looper
looper管理程序中的MessageQuere,負責迴圈訊息;
runnable
Runnable和Message可以被壓入某個MessageQueue中,形成一個集合 ;