高效能執行緒7. public class Task1 implements Runnable{
public void run(){
//蛋餅*1000 奶茶*1000
}
}
public class Task2 implements Runnable{
public void run(){
//饅頭*1 豆漿*1
}
}
//C,D的執行緒
//...
你可以這樣
買早餐
9. 執行緒不安全
(Thread Unsafe)
int mVar = 0;
public class Task1 implements Runnable{
public void run(){
mVar++;
}
}
public class Task2 implements Runnable{
public void run(){
mVar--;
}
}
11. ❖Mutual Exclusion (互斥)
➢ 任何一個時間點,最多只允許一個Process進入它自已 的C.S. 內活動,不允許多個Process同時 進
入各自的C.S.內活動。
❖Progress (行進) 須同時滿足下面2個要件:
➢ 1.不想進入C.S.的Process不可以阻礙其它Process進入C.S. (即: 不可參與進入C.S.之決策過程)
➢ 2.必須在有限的時間內,自那些想進入C.S.的Process之中,挑選 出一個Process進入C.S.
➢ (隱含:No Deadlock)
❖Bounded Waiting (有限等待)
➢ 自Process提出進入C.S.之申請,到它獲准進入C.S.之等待時間是有限的。 (隱含:No Starvation)
22. Handler
Handler是Android特有的機制, 透過他可以跟Main Thread進行溝通。
new Thread(new Runnable() {
public void run() {
//這邊是背景thread在運作, 這邊可以處理比較長時間或大量的運算
((Activity) mContext).runOnUiThread(new Runnable() {
public void run() {
//這邊是呼叫main thread handler幫我們處理UI部分
}
});
}
}).start();
view.post(new Runnable(){
public void run(){
//更新畫面
}
});
new Handler(mContext.getMainLooper()).post(new
Runnable(){
public void run(){
//處理少量資訊或UI
}
});
24. 實際上你可以自行定義Looper機制
new Thread(new Runnable() {
public void run() {
Log.e(TAG, "A");
Looper.prepare();
new Handler().post(new Runnable(){
@Override
public void run() {
Log.e(TAG, "B1");
}
});
new Handler().post(new Runnable(){
@Override
public void run() {
Log.e(TAG, "B2");
Looper.myLooper().quit();
}
});
輸出為:
● A
● B1
● B2
● C
● D
//接左邊
Looper.loop();
Log.e(TAG, "C");
((Activity) mContext).runOnUiThread(new Runnable() {
public void run() {
Log.e(TAG, "D");
}
});
}
}).start();
25. HandlerThread
Android實作了HandlerThread, 讓你輕鬆使用Looper機制。
HandlerThread handlerThread = new HandlerThread("HandlerThread");
handlerThread.start();
Handler mHandler = new Hanlder(handlerThread.getLooper()){
public void handleMessage(Message msg){
super.handleMessage(msg);
switch(msg.what){
case 1:
Logger.e("message receive");
break;
}
}
}
handler.sendEmptyMessage(1);
//或者
new Hanlder(handlerThread.getLooper())
.post(new Runnable(){
public void run(){
//長時間任務1
//長時間任務2
}
});
27. 記憶體洩漏(Memory leaks)
Dalvik VM是記憶體管理系統, 經常使用垃圾回收機制(Garbage Collection),
當一個物件不再使用, GC則會從Heap內將此物件移除回收,
如果該物件有被參照, 則將不會進行回收。
Thread如果沒寫好, 就很容易造成Memory leak。
可以透過LeakCanary這套工具來查看Memory leak。
28. 內部類別(Inner class)造成的Memory leak
public class Outer{
private OtherClass other;
//...
public void sample(){
SampleThread sampleThread = new SampleThread();
sampleThread.start();
}
private class SampleThread extends Thread{
public void run(){
Object sampleObject = new Object();
//long task
}
}
}
29. 靜態內部類別(Static inner class)
public class Outer{
private OtherClass other;
//...
public void sample(){
SampleThread sampleThread = new SampleThread();
sampleThread.start();
}
private static class SampleThread extends Thread{
public void run(){
Object sampleObject = new Object();
//long task
}
}
}
30. 將Runnable外包
可惜的是, 大多數程式設計師, 會將Runnable外包。
即便Thread是static, Runnable物件仍會參照到Outer物件, 造成memory leak。
public class Outer{
private Object o;
public Outer(){
SampleThread sampleThread = new SampleThread(new Runnable(){
public void run(){
o = new Object();
while(true);
}
});
sampleThread.start();
}
private static class SampleThread extends Thread{
public SampleThread(Runnable runnable){
super(runnable);
}
}
31. 事情沒有想像中簡單
在Android更多時候我們會這樣寫。
public class Outer{
Handler mHanlder = new Handler(){
public void handleMessage(Message msg){
//send message
}
};
public void doPost(){
mHanlder.post(new Runnable(){
public void run(){
//do long task
}
});
}
}
如果有參照到Outer內的物件, 只要Thread還在執行,
Outer物件就會memory leak。
32. 怎麼辦才好呢?
使用弱參考(Weak Reference)。
public class Outer{
private Object o;
private MyHandler mHandler;
public Outer(){
o = new Object();
mHandler = new MyHandler(this);
mHandler.post();
}
public Object getObj() {
return o;
}
如此一來, Outer物件一旦沒有參考, 則將被
視為可回收。
private static class MyHandler extends Handler {
private final WeakReference<Outer> mOuter;
public MyHandler(Outer outer){
mOuter = new WeakReference<Outer>(outer);
}
public void post(){
post(new Runnable() {
public void run() {
Outer out = (Outer) mOuter.get();
Object o = out.getObj();
while(true);
}
});
}
}
}
36. 參數說明
core pool size(核心緩衝池數量): Thread數量不會低於這個數字。
maxumum pool size(最大緩衝池數量): Thread pool的Thread最大數量
可根據底層硬體來決定數量。
int N = Runtime.getRuntime().availableProcessors();
keep-alive time(最大閒置時間): 超過閒置時間, 系統會回收core Thread數量以上的
Thread。
task queue type(任務佇列類型): 根據策略不同, 所用的演算法也會不同。
38. Callable搭配Future
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Object> future = executor.submit(new Callable<Object>(){
public Object call() throws Exception{
Object obj = doLongTask();
return obj;
}
});
Object result = future.get();
與Runnable不同的是Callable可以回傳結果,
透過blocking直到long task完成, 回傳物件。
42. 如何使用Asynctask ?
class MyTask extends AsyncTask<Params, Progress, Result>{
protected void onPreExecute(){
// in main thread
}
protected Result doInBackground(Params... params){
// in background thread
}
protected void onProgressUpdate(Progress... progress){
// in main thread
}
//接右邊
protected void onPostExecute(Result result){
// in main thread
}
protected void onCancelled(Result result){
// in main thread
}
}
範例-如何使用AsyncTask-實作
59. 啟動(Start)的Service
public class ServiceDemo extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
啟動的service非常簡單, 只要覆寫onBind並且回傳null, 接著再多覆寫
onStartCommand, 將Thread寫進此方法即可。
透過startService以及stopService控制啟動即結束,
也可以透過Service.stopSelf自行結束。
61. 連繫(Bound)的Service(一)
private LoaclServiceConnection mLoaclServiceConnection = new LoaclServiceConnection();
public class ServiceDemo extends Service {
private MyBinder mBinder = new MyBinder();
private class MyBinder extends Binder{
public ServiceDemo getService(){
return ServiceDemo.this;
}
}
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
}
62. 連繫(Bound)的Service(二)
private class LoaclServiceConnection implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//透過Binder調用Service內的方法
}
@Override
public void onServiceDisconnected(ComponentName name) {
//service 物件設為null
}
}
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
bindService(new Intent(this, ServiceDemo.class), mLoaclServiceConnection, Context.BIND_AUTO_CREATE);
}
}
68. 同步RPC(Remote Procedure Call)
如果你在aidl內定義
interface IMyAidlInterface {
String getThreadName();
}
server端以實作方式實現
private IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub(){
@Override
public String getThreadName() throws RemoteException {
return MyActivity.class.getName();
}
};
75. Binder vs. AIDL vs. Messenger
Binder主要是Android為了IPC而開發出來的。
AIDL跟Messenger最終還是會透過Binder進行IPC。
如果想要同時執行多個執行緒, 那麼AIDL是比較適合的。
如果只是想要傳遞訊息, 並且實做簡單並且執行緒安全, 則可以採用Messenger。