Android如何保证一个线程最多只能发出一个Looper?Android如何确保一个线程最多只能有一个Looper?

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

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为编制多线程并发程序提供了一个新的思绪。如下图所示,我们得以将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实现了线程本地存储。所有线程共享同一个ThreadLocal对象,但不同线程仅会顾与该线程相关联的价值,一个线程修改ThreadLocal对象对另线程没有影响。

ThreadLocal为编制多线程并发程序提供了一个新的笔触。如下图所示,我们可以ThreadLocal理解呢同片存储区,将即刻同样好块存储区分割为多片小之存储区,每一个线程拥有同等片属于自己的存储区,那么对协调之存储区操作就非见面影响其他线程。对于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中的位置,然后回来下一个职所蕴藏的靶子,即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的意义不错,但在差不多线程操作系统被,多线程并发可能导致一个线程设置的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。线程特定对象集可以储存于线程内部还是外部。Win32、Pthread和Java都针对线程特定数据产生支撑,这种气象下线程特定对象集可以储存于线程内部。
线程特定对象代理,受客户端能够像看常规对象同看线程特定对象。如果没有代理,客户端必须一直访问线程特定对象集并显示地使用键。相当给ThreadLocal<Looper>。

从今概念上称,可将Thread-Specific
Storage的构造即一个二维矩阵,每个键对应一行,每个线程对应一列。第k推行、第t列的矩阵元素呢对相应线程特定目标的指针。线程特定对象代理和线程特定对象集协作,向应用程序线程提供平等种访问第k履、第t排对象的平安机制。注意,这个模型只是类比。实际上Thread-Specific
Storage模式的落实并无是行使二维矩阵,因为键不肯定是邻近整数。

图片 8

 

  

 

4.2 Thread-Specific Storage模式的整体组织

图片 9

线程特定对象,相当于Looper。
线程特定目标集蕴含一组以及一定线程相关联的线程特定对象。每个线程都产生友好之线程特定目标集。相当给ThreadLocal.Values。线程特定对象集可以储存于线程内部还是外部。Win32、Pthread和Java都针对线程特定数据产生支撑,这种场面下线程特定对象集可以储存于线程内部。
线程特定对象代理,让客户端能够像看常规对象同看线程特定对象。如果没有代理,客户端必须一直看线程特定对象集并显示地使用键。相当给ThreadLocal<Looper>。

由概念上说,可拿Thread-Specific
Storage的构造即一个二维矩阵,每个键对应一行,每个线程对应一列。第k推行、第t排列的矩阵元素呢对相应线程特定对象的指针。线程特定目标代理和线程特定对象集协作,向应用程序线程提供平等种访问第k履、第t列对象的平安机制。注意,这个模型只是类比。实际上Thread-Specific
Storage模式的贯彻并无是运二维矩阵,因为键不必然是邻近整数。

图片 10