ThreadLocal存取的變量只能被當前線程訪問,其餘線程則沒法訪問和修改。html
數據以線程爲做用域,不一樣的線程有不一樣的數據副本。
各個線程往***同一個***ThreadLocal中填充的變量屬於當前線程,該變量對其餘線程而言是隔離的android
雖然是在不一樣線程調用的local
但由於存取的map變量是在Thread內部,而且local是同一個
因此,能實現存儲的變量屬於當前線程,對其它線程隔離數組
<!--#:ThreadLocal-->
/**
* 實際上調用的是:各個線程中的threadLocalMap.set/getEntry
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
//Thead中的ThreadLocalMap是在ThreadLocal中被建立的
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
--------------------
<!--#:Thread-->
ThreadLocal.ThreadLocalMap threadLocals = null;//在ThreadLocal中被初始化
//以local實例爲索引
private void set(ThreadLocal<?> key, Object value) {}
private Entry getEntry(ThreadLocal<?> key) {}
/**
雖然是在不一樣線程調用的local
但由於存取的map變量是在Thread內部,而且local是同一個
因此,能實現存儲的變量屬於當前線程,對其它線程隔離
*/
ThreadLocal<String> local = new ThreadLocal<>();
//#Thread$1
local.set("strValue");
String value = local.get();
//#Thread$2
local.set("strValue");
String value = local.get();
複製代碼
Android多線程:HandlerThread詳細使用手冊
Android多線程:一步步帶你源碼解析HandlerThread安全
# HandlerThread:
@Override
public void run() {
Looper.prepare();
synchronized (this) {
//這步纔是最主要的,讓處理耗時操做的workHandler使用mLooper,這樣就能在當前的子線程中處理workHandler的耗時操做了。
mLooper = Looper.myLooper();
notifyAll();
}
//空方法,loop前的準備
onLooperPrepared();
Looper.loop();
}
複製代碼
protected void onCreate() {
HandlerThread handlerThread = new HandlerThread("word_handler");
handlerThread.start();
//使用子線程的Looper,因此workHandler中的耗時操做在子線程中執行
WorkHandler workHandler = new WorkHandler(handlerThread.getLooper());
mainHandler = new MainHandler();
Message msg = Message.obtain();
msg.what = 1;
workHandler.sendMessage(msg);//work發送消息
}
//和HandlerThread同線程,處理工做任務
public class WorkHandler extends Handler {
@Override
public void handleMessage(Message msg) {
Message messag = Message.obtain();
messag.obj = "from word Handler";
mainHandler.sendMessage(messag);//通知主線程
}
}
class MainHandler extends Handler{
public void handleMessage(Message msg) {
tvTest.setText((String)msg.obj);
}
}
複製代碼
protected void initData() {
mainHandler = new MainHandler();
myHandlerThread = new WorkHandlerThread("my_handler_thread");
myHandlerThread.start();
}
@OnClick({R.id.btn_start})
public void onViewClicked(View view){
Message msg = Message.obtain();
msg.what = 1;
myHandlerThread.getWorkHandler().sendMessage(msg);
}
/**
* 當前線程
*/
class MainHandler extends Handler{
@Override
public void handleMessage(Message msg) {
tvTest.setText((String)msg.obj);
}
}
/**
* 工做:處理耗時任務
*/
class WorkHandlerThread extends MyHandlerThread{
public WorkHandlerThread(String name) {super(name);}
@Override
public void workMessage(Message msg) {
Thread.sleep(2000);
Message messag = Message.obtain();
messag.obj = "from word My WordHandler";
mainHandler.sendMessage(messag);
}
}
/**
* 封裝基類
*/
abstract class MyHandlerThread extends HandlerThread{
private WorkHandler mWorkHandler;
@Override
protected void onLooperPrepared() {
mWorkHandler = new WorkHandler(getLooper());
}
public WorkHandler getWorkHandler(){return mWorkHandler;}
public abstract void workMessage(Message msg);
class WorkHandler extends Handler{
public WorkHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
workMessage(msg);
}
}
}
複製代碼
IdleHandler-乾貨集中營
你知道android的MessageQueue.IdleHandler嗎?
IdleHandler,頁面啓動優化神器bash
是 Handler 機制提供的一種,能夠在 Looper 事件循環的過程當中,當出現空閒的時候,容許咱們執行任務的一種機制多線程
Message next() {
int pendingIdleHandlerCount = -1; // -1 only during first iteration
int nextPollTimeoutMillis = 0;
for (;;) {
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null) {
if (now < msg.when) {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// 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.
//第一次時知足<0條件
if (pendingIdleHandlerCount < 0
&& (mMessages == null || now < mMessages.when)) {
pendingIdleHandlerCount = mIdleHandlers.size();
}
//執行下面的for循環一次後,置爲0,後面再到此處再也不往下執行
if (pendingIdleHandlerCount <= 0) {
// No idle handlers to run. Loop and wait some more.
mBlocked = true;
continue;
}
}
// We only ever reach this code block during the first iteration.
//只有第一次執行能到達
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
//置爲0,後面再也不知足上面條件進入for循環
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;
}
}
複製代碼
Android Handler機制之總目錄
Message中obtain()與recycle()的前因後果-系列異步
Android的消息機制主要是指Handler的運行機制
即:消息的發送、入隊、出隊、分發過程。async
Android規定訪問UI只能在主線程中進行,在子線程中訪問UI就會拋異常。可是Android又不建議在主線程中作耗時操做,會可能致使ANR。因此,咱們須要,能在子線程中作完耗時操做,而後去到主線程更新UI的辦法。
Hander的主要做用是將一個任務切換到指定的線程中去執行。所以,系統提供Handler主要是***爲了解決在子線程中沒法訪問UI的問題***(而不是把耗時操做放到子線程中的問題)。ide
在單線程模型中始終要記住兩條法則:
一、不要阻塞UI線程
二、確保只在UI線程中訪問UI
handler中有衆多的send方法,時間點的區別而已,到最後都會調用下面的方法,把message放入消息隊列
MessageQueue是鏈表結構
//消息發送
<!--#Handler-->
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//注意此處,this指的是handler本身
msg.target = this;
//把消息插入隊列
return queue.enqueueMessage(msg, uptimeMillis);
}
//入隊
<!--#MessageQueue-->
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
//若是隊列爲空或者時間最小,插入到頭部
if (p == null || when == 0 || when < p.when) {
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
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;
}
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
複製代碼
loop將隊列中的msg一個一個取出,分發到各自的handler中處理。
handler根據是否有callback選擇不一樣的分發方式。
//出隊
<!--#Looper-->
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
//真正決定阻塞的是queue.next()中的for循環
for (;;) {
//next方法會阻塞線程,有消息就取出,沒消息就等待。退出返回null結束當前循環。
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
//Looper.quit退出循環
return;
}
//加入消息隊列時,target == handler,分發的時候就能夠找到原來的handler,讓其本身處理。
//因此,一個線程多個handler發送消息,雖然都在一個隊列裏,可是仍是會分發到原來的handler處理消息。
msg.target.dispatchMessage(msg);
}
}
//取出消息
<!--#MessageQueue-->
Message next() {
//阻塞線程
for (;;) {
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) {
//異步消息?
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
//還不到取出時間
if (now < msg.when) {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 取出msg
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
//Looper.quit(),返回null。
//此時,Looper中的for循環收到null也return,退出循環
if (mQuitting) {
dispose();
return null;
}
}
}
}
//由target返回到Handler中執行分發
//加入隊列的:有的是Runnable封裝的message,有的是callback,有的是message,在這裏分發
<!--#Handler-->
public void dispatchMessage(Message msg) {
//runnable封裝的message。 handler.post/view.post
if (msg.callback != null) {//msg中的callback 優先級1
handleCallback(msg);
} else {
if (mCallback != null) {
//Handler(Callback callback)的時候,mCallback !=null
if (mCallback.handleMessage(msg)) {//handler中的callback,優先級2
return;
}
}
//正常的message那種形式,也便是new Handler時複寫的方法。
handleMessage(msg);// 優先級3
}
}
複製代碼
<!--#Looper-->
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
<!--#Handler-->
public Handler(Callback callback, boolean async) {
//驗證當前線程有沒有Looper對象,因此new以前要prepare存入
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
}
<!--#:Thread1-->
Looper.prepare()//當前線程存
Handler handler = new Handler();//當前線程取
Looper.loop();
<!--#:Thread2-->
Looper.prepare()//當前線程存
Handler handler = new Handler();//當前線程取
Looper.loop();
<!--#:Thread3-->
Looper.prepare()//當前線程存
Handler handler = new Handler();//當前線程取
Looper.loop();
複製代碼
爲何主線程 new Handler以前不用prepare,也不用loop?
UI線程是主線程,系統已經自動幫咱們調用了Looper.prepare()和Looper.loop()方法
複製代碼
優先級1:
<!--封裝成Message,放入隊列-->
//view.post(runnable)和handler.post(runnable)
//最後runnable都會分裝成Message。分發的時候,在第一處調用。
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
<!--分發調用-->
private static void handleCallback(Message message) {
message.callback.run();
}
注意:雖然是runnable,但並非新的線程。
此處的run()就是正常的方法,Runnable中不要作耗時操做。
複製代碼
優先級2:
public Handler(Callback callback, boolean async) {
...
mCallback = callback;
...
}
複製代碼
優先級3:
經常使用的發送Message方式,設置what,obj參數,發送的消息。
<!--#MessageQueue-->
void quit(boolean safe) {
//主線程時,quit報錯
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
synchronized (this) {
//mQuitting該標記使next方法中返回null,使loop結束循環。
mQuitting = true;
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
}
}
Message next() {
//MessageQueue的next返回null,就能結束loop循環了
if (mQuitting) {
dispose();
return null;
}
}
public static void loop() {
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next();
/*
* 不是指消息隊列裏沒有消息了(沒有消息,隊列也會一直循環)
* 是調用了Loop的quit()、quitSafely()方法,結束循環
*/
if (msg == null) {
//退出循環
return;
}
...
}
}
複製代碼
<!--#MessageQueue-->
void quit(boolean safe) {
if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}
}
//回收全部消息
private void removeAllMessagesLocked() {
Message p = mMessages;
while (p != null) {
Message n = p.next;
p.recycleUnchecked();
p = n;
}
mMessages = null;
}
//回收延遲消息
private void removeAllFutureMessagesLocked() {
final long now = SystemClock.uptimeMillis();
Message p = mMessages;
if (p != null) {
if (p.when > now) {
removeAllMessagesLocked();
} else {
Message n;
for (;;) {
n = p.next;
if (n == null) {
return;
}
if (n.when > now) {
break;
}
//找到 <= now的msg ==> p
p = n;
}
//把p之後的消息回收
p.next = null;
do {
p = n;
n = p.next;
p.recycleUnchecked();
} while (n != null);
}
}
}
複製代碼
quit()方法的做用:
1_標記mQuitting = true,使next方法返回null,結束循環。
2_移除消息隊列中的消息。
源碼中能夠看到,子線程中preaper時候,默認設置爲true。
主線程中:prepareMainLooper的時候,默認是false。
因此,子線程的Loop是能夠quit的,而主線程的不能夠。
//app入口 ActivityThread
public static void main(String[] args) {
...
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
...
Looper.loop();
}
public static void prepareMainLooper() {
prepare(false);
...
}
//初始化MessageQueue時設置
MessageQueue(boolean quitAllowed) {
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
void quit(boolean safe) {
//這個地方:主線程時,quit報錯
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}
...
}
複製代碼
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
//非UI線程,到主線程的handler執行
mHandler.post(action);
} else {
//UI線程直接run
action.run();
}
}
複製代碼
Handler內部類引用Activity,msg.target = this引用handler,形成內存泄漏
/**
爲避免handler形成的內存泄漏
一、使用靜態的handler,對外部類不保持對象的引用
二、但Handler須要與Activity通訊,因此須要增長一個對Activity的弱引用
*/
private static class MyHandler extends Handler {
private final WeakReference<Activity> mActivityReference;
MyHandler(Activity activity) {
this.mActivityReference = new WeakReference<Activity>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
MainActivity activity = (MainActivity) mActivityReference.get(); //獲取弱引用隊列中的activity
//若引用,判斷null
if(activity != null && !activity.isFinish()){
byte[] data = (byte[]) msg.obj;
activity.threadIv.setImageBitmap(activity.getBitmap(data));
break;
}
}
複製代碼
Android中爲何主線程不會由於Looper.loop()裏的死循環卡死
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler();
Looper.loop();
Log.d("loop()是死循環,阻塞線程,後面的代碼不執行");
}
}
複製代碼
#ActivityThread:
public static void main(String[] args) {
...
Looper.prepareMainLooper();
#1
ActivityThread thread = new ActivityThread();
#2
thread.attach(false);
if (sMainThreadHandler == null) {
#3
sMainThreadHandler = thread.getHandler();
}
#4
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
複製代碼
Android的各個生命週期和事件,都是在loop循環中接收、處理消息的。引發ANR是由於處理消息時阻塞了,而不是由於loop循環阻塞的。
其實否則,這裏就涉及到Linux pipe/epoll機制;
簡單說就是在主線程的MessageQueue沒有消息時,便阻塞在loop的queue.next()中的nativePollOnce()方法裏,詳情見Android消息機制1-Handler(Java層),此時主線程會釋放CPU資源進入休眠狀態,直到下個消息到達或者有事務發生,經過往pipe管道寫端寫入數據來喚醒主線程工做。
這裏採用的epoll機制,是一種IO多路複用機制,能夠同時監控多個描述符,當某個描述符就緒(讀或寫就緒),則馬上通知相應程序進行讀或寫操做,本質同步I/O,即讀寫是阻塞的。
因此說,主線程大多數時候都是處於休眠狀態,並不會消耗大量CPU資源。
#HandlerActionQueue:
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
//若是當前View加入到了window中,直接調用UI線程的Handler發送消息
return attachInfo.mHandler.post(action);
}
//View未加入到window,放入HandlerActionQueue的mActions(數組)中
getRunQueue().post(action);
return true;
}
#View:
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
//View加入window後,直接執行mActions保存的
mRunQueue.executeActions(info.mHandler);
}
複製代碼