落实Looper.prepare()这么些艺术,假诺以往要我们来完毕Looper.prepare()那一个格局

1. 如何创建Looper?

Looper的构造方法为private,所以无法一贯动用其构造方法创制。

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

要想在方今线程创建Looper,需利用Looper的prepare方法,Looper.prepare()。

如果明天要大家来贯彻Looper.prepare()那些方法,咱们该怎么做?大家明白,Android中七个线程最三只好有2个Looper,若在已有Looper的线程中调用Looper.prepare()会抛出RuntimeException(“Only
one Looper may be created per
thread”)。面对诸如此类的急需,大家或者会设想使用3个HashMap,个中Key为线程ID,Value为与线程关联的Looper,再拉长某个同台机制,实现Looper.prepare()那么些艺术,代码如下:

public class Looper {

    static final HashMap<Long, Looper> looperRegistry = new HashMap<Long, Looper>();

    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));
        }
    }
    ...
}

1. 怎么样创设Looper?

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<Long, Looper> looperRegistry = new HashMap<Long, Looper>();

    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));
        }
    }
    ...
}

2. ThreadLocal

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为编写二十三八线程并发程序提供了3个新的笔触。如下图所示,大家得以将ThreadLocal掌握为一块存款和储蓄区,将这一大块存款和储蓄区分割为多块小的存款和储蓄区,每四个线程拥有一块属于本身的存储区,那么对协调的存储区操作就不会影响其余线程。对于ThreadLocal<Looper>,则每一小块存储区中就封存了与特定线程关联的Looper。
图片 1

2. ThreadLocal

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完毕了线程本地存款和储蓄。全体线程共享同2个ThreadLocal对象,但不相同线程仅能访问与其线程相关联的值,八个线程修改ThreadLocal对象对其他线程没有影响。

ThreadLocal为编写制定二十多线程并发程序提供了二个新的思绪。如下图所示,大家可以将ThreadLocal领悟为一块存款和储蓄区,将这一大块存款和储蓄区分割为多块小的存款和储蓄区,每2个线程拥有一块属于自己的存款和储蓄区,那么对自身的存款和储蓄区操作就不会影响别的线程。对于ThreadLocal<Looper>,则每一小块存款和储蓄区中就保存了与一定线程关联的Looper。
图片 2

3. ThreadLocal的中间贯彻原理

3. ThreadLocal的内部贯彻原理

3.1 Thread、ThreadLocal和Values的关系

Thread的分子变量localValues代表了线程特定变量,类型为ThreadLocal.Values。由于线程特定变量恐怕会有多少个,并且类型不分明,所以ThreadLocal.Values有一个table成员变量,类型为Object数组。这一个localValues能够领略为二维存储区中与一定线程相关的一列。
ThreadLocal类则一定于二个代理,真正操作线程特定期存款款和储蓄区table的是其里面类Values。
图片 3
图片 4

3.1 Thread、ThreadLocal和Values的关系

Thread的积极分子变量localValues代表了线程特定变量,类型为ThreadLocal.Values。由于线程特定变量大概会有两个,并且类型不显明,所以ThreadLocal.Values有二个table成员变量,类型为Object数组。那几个localValues能够知道为二维存款和储蓄区中与特定线程相关的一列。
ThreadLocal类则也便是二个代理,真正操作线程特定期存款款和储蓄区table的是其内部类Values。
图片 5
图片 6

3.2 set方法

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

3.2 set方法

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

3.3 get方法

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为消除二十八线程程序的产出难点提供了一种新的思绪。

3.3 get方法

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中的地点,然后回来下3个岗位所蕴藏的靶子,即ThreadLocal的值,在Looper那些例子中便是与当下线程关联的Looper对象。

从set和get方法能够看出,其所操作的都以当前线程的localValues中的table数组,所以不一样线程调用同一个ThreadLocal对象的set和get方法互不影响,那正是ThreadLocal为化解四线程程序的出现难题提供了一种新的思绪。

4. ThreadLocal背后的筹划思想Thread-Specific Storage形式

Thread-Specific
Storage让多少个线程能够采取同样的”逻辑全局“访问点来收获线程本地的靶子,幸免了每一趟访问对象的锁定花费。

4. ThreadLocal背后的规划思想Thread-Specific Storage情势

Thread-Specific
Storage让四个线程能够选用相同的”逻辑全局“访问点来取得线程本地的指标,防止了历次访问对象的锁定花费。

4.1 Thread-Specific Storage格局的根源

errno机制被广大用于一些操作系统平台。errno
是记录系统的结尾一回错误代码。对于单线程程序,在大局意义域内完结errno的效应不错,但在四线程操作系统中,多线程并发恐怕引致1个线程设置的errno值被别的线程错误解读。当时众多遗留库和应用程序都是依照单线程编写,为了在不修改既有接口和遗留代码的情况下,化解二十四线程访问errno的难点,Thread-Specific
Storage格局诞生。

4.1 Thread-Specific Storage方式的源于

errno机制被大面积用于一些操作系统平台。errno
是记录系统的结尾壹次错误代码。对于单线程程序,在大局意义域内达成errno的效应不错,但在八线程操作系统中,十二线程并发可能造成一个线程设置的errno值被此外线程错误解读。当时游人如织遗留库和应用程序都以根据单线程编写,为了在不改动既有接口和遗留代码的情事下,化解二十四线程访问errno的题材,Thread-Specific
Storage形式诞生。

4.2 Thread-Specific Storage方式的完全协会

图片 7

线程特定对象,相当于Looper。
线程特定目的集含蓄一组与一定线程相关联的线程特定对象。各种线程都有和好的线程特定指标集。相当于ThreadLocal.Values。线程特定对象集能够储存在线程内部或外部。Win3② 、Pthread和Java都对线程特定数据有帮忙,这种意况下线程特定指标集能够储存在线程内部。
线程特定对象代理,让客户端能够像访问常规对象一样访问线程特定对象。借使没有代理,客户端必须一直访问线程特定指标集并出示地使用键。约等于ThreadLocal<Looper>。

从概念上讲,可将Thread-Specific
Storage的构造视为二个二维矩阵,各样键对应一行,每一个线程对应一列。第k行、第t列的矩阵成分为指向相应线程特定指标的指针。线程特定对象代理和线程特定对象集合作,向应用程序线程提供一种访问第k行、第t列对象的长治体制。注意,这么些模型只是类比。实际上Thread-Specific
Storage格局的落到实处并不是应用二维矩阵,因为键不自然是邻近整数。

图片 8

 

  

 

4.2 Thread-Specific Storage方式的欧洲经济共同体组织

图片 9

线程特定目标,相当于Looper。
线程特定目的集含蓄一组与一定线程相关联的线程特定指标。每一种线程都有协调的线程特定对象集。约等于ThreadLocal.Values。线程特定目标集能够储存在线程内部或外部。Win3② 、Pthread和Java都对线程特定数据有支撑,那种景色下线程特定对象集能够储存在线程内部。
线程特定指标代理,让客户端能够像访问常规对象一样访问线程特定指标。假若没有代理,客户端必须向来访问线程特定指标集并出示地使用键。也便是ThreadLocal<Looper>。

从概念上讲,可将Thread-Specific
Storage的结构视为四个二维矩阵,各个键对应一行,每一个线程对应一列。第k行、第t列的矩阵元素为指向相应线程特定对象的指针。线程特定指标代理和线程特定指标集同盟,向应用程序线程提供一种访问第k行、第t列对象的云浮机制。注意,那几个模型只是类比。实际上Thread-Specific
Storage方式的贯彻并不是行使二维矩阵,因为键不自然是隔壁整数。

图片 10

 

  

 

相关文章