设为首页 加入收藏

TOP

Android如何保证一个线程最多只能有一个Looper?(一)
2017-10-13 10:12:01 】 浏览:9544
Tags: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));
        }
    }
    ...
}

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。
这里写图片描述

3. ThreadLocal的内部实现原理

3.1 Thread、ThreadLocal和Values的关系

Thread的成员变量localValues代表了线程特定变量,类型为ThreadLocal.Values。由于线程特定变量可能会有多个,并且类型不确定,所以ThreadLocal.Values有一个table成员变量,类型为Object数组。这个localValues可以理解为二维存储区中与特定线程相关的一列。
ThreadLocal类则相当于一个代理,真正操作线程特定存储区table的是其内部类Values。
这里写图片描述
这里写图片描述

3.2 set方法

public void set(T value) {
    Thread currentThread = Thread.currentThread();
    Values values = values(currentThread);
    if (values == null) {
        values = initializeva lues(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(
首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Android如何保证一个线程最多只能.. 下一篇录制Android手机操作,转换为Gif图

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目