編輯:關於Android編程
Looper的構造方法為private,所以不能直接使用其構造方法創建。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
要想在當前線程創建Looper,需使用Looper的prepare方法,Looper.prepare()。
如果現在要我們來實現Looper.prepare()這個方法,我們該怎麼做?我們知道,Android中一個線程最多只能有一個Looper,若在已有Looper的線程中調用Looper.prepare()會拋出RuntimeException(“Only one Looper may be created per thread”)。面對這樣的需求,我們可能會考慮使用一個HashMap,其中Key為線程ID,Value為與線程關聯的Looper,再加上一些同步機制,實現Looper.prepare()這個方法,代碼如下:
public class Looper {
static final HashMap looperRegistry = new HashMap();
private static void prepare() {
synchronized(Looper.class) {
long currentThreadId = Thread.currentThread().getId();
Looper l = looperRegistry.get(currentThreadId);
if (l != null)
throw new RuntimeException("Only one Looper may be created per thread");
looperRegistry.put(currentThreadId, new Looper(true));
}
}
...
}
上述方法對Looper.class對象進行了加鎖,這些加鎖開銷有可能造成性能瓶頸。
有沒有更好的方法實現Looper.prepare()方法?看一看Android的中Looper的源碼。
public class Looper {
static final ThreadLocal sThreadLocal = new ThreadLocal();
public static void prepare() {
prepare(true);
}
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));
}
...
}
prepare()方法中調用了ThreadLocal的get和set方法,然而整個過程沒有添加同步鎖,Looper是如何實現線程安全的?
ThreadLocal位於java.lang包中,以下是JDK文檔中對該類的描述
Implements a thread-local storage, that is, a variable for which each thread has its own value. All threads share the same ThreadLocal object, but each sees a different value when accessing it, and changes made by one thread do not affect the other threads. The implementation supports null values.
大致意思是,ThreadLocal實現了線程本地存儲。所有線程共享同一個ThreadLocal對象,但不同線程僅能訪問與其線程相關聯的值,一個線程修改ThreadLocal對象對其他線程沒有影響。
ThreadLocal為編寫多線程並發程序提供了一個新的思路。如下圖所示,我們可以將ThreadLocal理解為一塊存儲區,將這一大塊存儲區分割為多塊小的存儲區,每一個線程擁有一塊屬於自己的存儲區,那麼對自己的存儲區操作就不會影響其他線程。對於ThreadLocal
Thread的成員變量localValues代表了線程特定變量,類型為ThreadLocal.Values。由於線程特定變量可能會有多個,並且類型不確定,所以ThreadLocal.Values有一個table成員變量,類型為Object數組。這個localValues可以理解為二維存儲區中與特定線程相關的一列。
ThreadLocal類則相當於一個代理,真正操作線程特定存儲區table的是其內部類Values。
public void set(T value) {
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values == null) {
values = initializeValues(currentThread);
}
values.put(this, value);
}
Values values(Thread current) {
return current.localValues;
}
既然與特定線程相關,所以先獲取當前線程,然後獲取當前線程特定存儲,即Thread中的localValues,若localValues為空,則創建一個,最後將value存入values中。
void put(ThreadLocal key, Object value) {
cleanUp();
// Keep track of first tombstone. That's where we want to go back
// and add an entry if necessary.
int firstTombstone = -1;
for (int index = key.hash & mask;; index = next(index)) {
Object k = table[index];
if (k == key.reference) {
// Replace existing entry.
table[index + 1] = value;
return;
}
if (k == null) {
if (firstTombstone == -1) {
// Fill in null slot.
table[index] = key.reference;
table[index + 1] = value;
size++;
return;
}
// Go back and replace first tombstone.
table[firstTombstone] = key.reference;
table[firstTombstone + 1] = value;
tombstones--;
size++;
return;
}
// Remember first tombstone.
if (firstTombstone == -1 && k == TOMBSTONE) {
firstTombstone = index;
}
}
}
從put方法中,ThreadLocal的reference和值都會存進table,索引分別為index和index+1。
對於Looper這個例子,
table[index] = sThreadLocal.reference;(指向自己的一個弱引用)
table[index + 1] = 與當前線程關聯的Looper。
public T get() {
// Optimized for the fast path.
Thread currentThread = Thread.currentThread();
Values values = values(currentThread);
if (values != null) {
Object[] table = values.table;
int index = hash & values.mask;
if (this.reference == table[index]) {
return (T) table[index + 1];
}
} else {
values = initializeValues(currentThread);
}
return (T) values.getAfterMiss(this);
}
首先取出與線程相關的Values,然後在table中尋找ThreadLocal的reference對象在table中的位置,然後返回下一個位置所存儲的對象,即ThreadLocal的值,在Looper這個例子中就是與當前線程關聯的Looper對象。
從set和get方法可以看出,其所操作的都是當前線程的localValues中的table數組,所以不同線程調用同一個ThreadLocal對象的set和get方法互不影響,這就是ThreadLocal為解決多線程程序的並發問題提供了一種新的思路。
Thread-Specific Storage讓多個線程能夠使用相同的”邏輯全局“訪問點來獲取線程本地的對象,避免了每次訪問對象的鎖定開銷。
errno機制被廣泛用於一些操作系統平台。errno 是記錄系統的最後一次錯誤代碼。對於單線程程序,在全局作用域內實現errno的效果不錯,但在多線程操作系統中,多線程並發可能導致一個線程設置的errno值被其他線程錯誤解讀。當時很多遺留庫和應用程序都是基於單線程編寫,為了在不修改既有接口和遺留代碼的情況下,解決多線程訪問errno的問題,Thread-Specific Storage模式誕生。
線程特定對象<喎?/kf/ware/vc/" target="_blank" class="keylink">vc3Ryb25nPqOsz+C1sdPaTG9vcGVyoaM8YnIgLz4NCjxzdHJvbmc+z9+zzMzYtqi21M/zvK88L3N0cm9uZz6w/Lqs0rvX6dPrzNi2qM/fs8zP4LnYwaq1xM/fs8zM2LaottTP86Gjw7+49s/fs8y2vNPQ19S8urXEz9+zzMzYtqi21M/zvK+ho8/gtbHT2lRocmVhZExvY2FsLlZhbHVlc6Gjz9+zzMzYtqi21M/zvK+/ydLUtOa0otTaz9+zzMTasr+78s3isr+ho1dpbjMyoaJQdGhyZWFkus1KYXZhtry21M/fs8zM2Laoyv2+3dPQ1qez1qOs1eLW1sfpv/bPws/fs8zM2LaottTP87yvv8nS1LTmtKLU2s/fs8zE2rK/oaM8YnIgLz4NCjxzdHJvbmc+z9+zzMzYtqi21M/ztPrA7aOsPC9zdHJvbmc+yMO/zbuntsvE3Lm7z/G3w87Ks6O55rbUz/PSu9H5t8POys/fs8zM2LaottTP86GjyOe5+8O709C0+sDto6y/zbuntsux2NDr1rG907fDzsrP37PMzNi2qLbUz/O8r7Kiz9TKvrXYyrnTw7z8oaPP4LWx09pUaHJlYWRMb2NhbDxsb29wZXI+oaM8L2xvb3Blcj48L3A+DQo8cD6007jFxO7Jz72yo6y/yb2rVGhyZWFkLVNwZWNpZmljIFN0b3JhZ2W1xL3hubnK086q0ru49rb+zqy+2NXzo6zDv7j2vPy21NOm0rvQ0KOsw7+49s/fs8y21NOm0rvB0KGjtdpr0NChorXadMHQtcS+2NXz1KrL2M6q1rjP8s/g06bP37PMzNi2qLbUz/O1xNa41euho8/fs8zM2LaottTP87T6wO26zc/fs8zM2LaottTP87yv0K3X96Osz/LTptPDs8zQ8s/fs8zM4bmp0rvW1rfDzsq12mvQ0KGitdp0wdC21M/ztcSwssiru/rWxqGj16LS4qOs1eK49sSj0M3Wu8rHwOCxyKGjyrW8ysnPVGhyZWFkLVNwZWNpZmljIFN0b3JhZ2XEo8q9tcTKtc/WsqKyu8rHyrnTw7b+zqy+2NXzo6zS8s6qvPyyu9K7tqjKx8/gwdrV+8r9oaM8YnIgLz4NCjxpbWcgYWx0PQ=="這裡寫圖片描述" src="https://www.android5.online/Android/UploadFiles_5356/201702/2017022313401418.png" title="\" />
前言寫Android:如何編寫“萬能”的Activity的這篇文章到現在已經好久了,但是由於最近事情較多,寫重構篇的計劃就一直被無情的耽擱下來了,借這幾天還算有點空余時間
一基礎知識 android的事件處理分為3步。 1)public booleandispatchTouchEvent(MotionEvent ev) 這個方法用來分發
console是構建在stdio之上的,console的初始化是board_r中最後收尾的操作。console的初始化函數console_init_r在common/co
首先,登陸到Github上並創建一個新repository。在屏幕右上角,點擊“+”標記,並且選擇“New repository&rd