Android -理解ThreadLocal、Looper

来源:互联网 发布:安全网络龙头股 编辑:程序博客网 时间:2024/05/06 05:51

一、理解Looper:消息循环

参考:android的消息处理机制(图+源码分析)——Looper,Handler,Message

Android消息循环是针对线程的(每个线程都可以有自己的消息队列和消息循环)。

Looper用于封装了android线程中的消息循环,默认情况下一个线程是不存在消息循环(message loop)的,需要调用Looper.prepare()来给线程创建一个消息循环,调用Looper.loop()来使消息循环起作用,从消息队列里取消息,处理消息。

Activity的MainUI线程默认是有消息队列的。所以在Activity中新建Handler时,不需要先调用Looper.prepare()。

注:写在Looper.loop()之后的代码不会被立即执行,当调用后 mHandler.getLooper().quit()后,loop才会中止,其后的代码才能得以运行。Looper对象通过MessageQueue 来存放消息和事件。一个线程只能有一个Looper,对应一个MessageQueue。一个线程可以有多个Handler,但是只能有一个Looper!

Android系统中Looper负责管理线程的消息队列和消息循环。
可以通过Loop.myLooper()得到当前线程的Looper对象,
通过Loop.getMainLooper()可以获得当前进程的主线程的Looper对象。

例如常见刷新界面:

    private void invalidateView() {        //Looper.getMainLooper返回进程的主线程(UI线程)中Looper对象        //Looper.myLooper返回当前线程的Looper对象        if (Looper.getMainLooper() == Looper.myLooper()) {//如果当前线程是主线程            invalidate();        } else {            postInvalidate();        }    }

Looper 的源码:

public class Looper {    // 每个线程中的Looper对象其实是一个ThreadLocal,即线程本地存储(TLS)对象    private static final ThreadLocal sThreadLocal = new ThreadLocal();    // Looper内的消息队列    final MessageQueue mQueue;    // 当前线程    Thread mThread;    // 。。。其他属性    // 每个Looper对象中有它的消息队列,和它所属的线程    private Looper() {        mQueue = new MessageQueue();//每个looper有自己的MessageQueue        mRun = true;        mThread = Thread.currentThread();//每个looper有自己的Thread    }    // 我们调用该方法会在调用线程的TLS中创建Looper对象    public static final void prepare() {        if (sThreadLocal.get() != null) {            // 试图在有Looper的线程中再次创建Looper将抛出异常            throw new RuntimeException("Only one Looper may be created per thread");        }        sThreadLocal.set(new Looper());//将looper对象设置为ThreadLocalMap的value值    }    // 其他方法}

二、理解ThreadLocal:线程局部变量

参考:ThreadLocal详解

在并发编程的时候,成员变量如果不做任何处理其实是线程不安全的,各个线程都在操作同一个变量,显然是不行的,并且我们也知道volatile这个关键字也是不能保证线程安全的。那么在有一种情况之下,我们需要满足这样一个条件:变量是同一个,但是每个线程都使用同一个初始值,也就是使用同一个变量的一个新的副本。这种情况之下ThreadLocal就非常使用,比如说DAO的数据库连接,我们知道DAO是单例的,那么他的属性Connection就不是一个线程安全的变量。而我们每个线程都需要使用他,并且各自使用各自的。这种情况,ThreadLocal就比较好的解决了这个问题。

应用场景:当很多线程需要多次使用同一个对象,并且需要该对象具有相同初始化值的时候最适合使用ThreadLocal。

当我们调用set()方法的时候,很常规,就是将值设置进ThreadLocal中。

当我们调用get方法的时候,其实每个当前线程中都有一个ThreadLocal。每次获取或者设置都是对该ThreadLocal进行的操作,是与其他线程分开的。

从本质来讲,就是每个线程都维护了一个map,而这个map的key就是threadLocal,而值就是我们set的那个值,每次线程在get的时候,都从自己的变量中取值,既然从自己的变量中取值,那肯定就不存在线程安全问题,总体来讲,ThreadLocal这个变量的状态根本没有发生变化,他仅仅是充当一个key的角色,另外提供给每一个线程一个初始值。如果允许的话,我们自己就能实现一个这样的功能,只不过恰好JDK就已经帮我们做了这个事情。

1、每个线程thread中都有一个自己的ThreadLocalMap类对象,可以将线程自己的对象保持到其中,各管各的,线程可以正确的访问到自己的对象。

2、将一个共用的ThreadLocal静态实例作为key,将不同对象的引用保存到不同线程的ThreadLocalMap中,然后在线程执行的各处通过这个静态ThreadLocal实例的get()方法取得自己线程保存的那个对象,避免了将这个对象作为参数传递的麻烦。

Thread 部分源码:

public class Thread implements Runnable {    //ThreadLocalMap     ThreadLocal.ThreadLocalMap threadLocals = null;    //ThreadLocalMap     ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;}

ThreadLocal部分源码:

ThreadLocalMap 为ThreadLocal的内部类。

/** * 线程局部变量。 * * 这些变量与正常变量不同。因为每一个访问线程的变量(通过get或set方法)都有自己线程的变量,独立初始化变量的副本。 * ThreadLocal实例通常是私有的静态字段在希望关联状态的线程的类中(例如,一个用户ID或交易ID)。 * * 例如:下面的类产生本地唯一的标识符在每个线程中。 * 一个线程的id在第一次调用 ThreadId.get() 时就分配,在后续调用保持不变。 * import java.util.concurrent.atomic.AtomicInteger; * * public class ThreadId { * // Atomic integer containing the next thread ID to be assigned * private static final AtomicInteger nextId = new AtomicInteger(0); * <p> * // Thread local variable containing each thread's ID * private static final ThreadLocal&lt;Integer> threadId = * new ThreadLocal&lt;Integer>() { * &#64;Override protected Integer initialValue() { * return nextId.getAndIncrement(); * } * }; * <p> * // Returns the current thread's unique ID, assigning it if necessary * public static int get() { * return threadId.get(); * } * } * <p> * 每个线程只要在线程存活且ThreadLocal实例可访问,都持有线程本地变量的隐式引用 * <p> * 在线程消失后,线程本地实例的所有副本都将受到垃圾回收(除非对这些副本的其他引用存在)。 */public class ThreadLocal<T> {    /**     * 创建线程本地变量     */    public ThreadLocal() {    }    /**     * 返回当前线程的局部线程变量的值。     * <p>     * 如果当前线程变量没有值,它首先初始化为调用初始化值方法返回的值。     *     * @return 返回本地线程当前线程的值     */    public T get() {        Thread t = Thread.currentThread();//当前线程        ThreadLocalMap map = getMap(t);//当前线程的map        if (map != null) {            ThreadLocalMap.Entry e = map.getEntry(this);//当前线程的map的key为ThreadLocal            if (e != null)                return (T) e.value;        }        return setInitialValue();    }    /**     * 将当前线程的本地变量的副本设置为指定的值。     * <p>     * 大多数子类将不需要重写此方法,只依赖于方法设置线程本地值。     *     * @param value 要存储在当前线程本地线程副本中的值。     */    public void set(T value) {        Thread t = Thread.currentThread();//当前线程        ThreadLocalMap map = getMap(t);//当前线程的map        if (map != null)            map.set(this, value);//当前线程的map的key为ThreadLocal        else            createMap(t, value);    }    /**     * 为该线程局部变量移除当前线程的值。     * <p>     * 如果此线程局部变量随后由当前线程执行,它的值将通过调用其方法被重新初始化     * <p>     * 除非其值由临时线程中的当前线程决定。这可能会导致当前线程在多个调用初值的方法。     *     * @since 1.5     */    public void remove() {        ThreadLocalMap m = getMap(Thread.currentThread());        if (m != null)            m.remove(this);    }    /**     * 获取一个ThreadLocal相关的map。在继承ThreadLocal中重写。     *     * @param t 当前线程     * @return 获取的map     */    ThreadLocalMap getMap(Thread t) {        return t.threadLocals;    }    /**     *     * 创建一个ThreadLocal相关的map。在继承ThreadLocal中重写。     *     * @param t          当前线程     * @param firstValue 初始化值     * @param map        存储的map     */    void createMap(Thread t, T firstValue) {        t.threadLocals = new ThreadLocalMap(this, firstValue);    }    /**     *     * threadlocalmap是一个定制的哈希映射只适合维护线程局部变量的值。     *     *没有操作出口在ThreadLocal类之外。     * 类是包私有的,允许在类线程中声明字段。     * 为了帮助处理非常大且寿命长的用法,哈希表条目使用使用弱引用键。     * 但是,由于不使用引用队列,所以只有在表开始耗尽空间时才保证删除过期条目。     */    static class ThreadLocalMap {        static class Entry extends WeakReference<ThreadLocal> {            Object value;            Entry(ThreadLocal k, Object v) {                super(k);                value = v;            }        }        ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {            table = new Entry[INITIAL_CAPACITY];            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);            table[i] = new Entry(firstKey, firstValue);            size = 1;            setThreshold(INITIAL_CAPACITY);        }        private ThreadLocalMap(ThreadLocalMap parentMap) {            Entry[] parentTable = parentMap.table;            int len = parentTable.length;            setThreshold(len);            table = new Entry[len];            for (int j = 0; j < len; j++) {                Entry e = parentTable[j];                if (e != null) {                    ThreadLocal key = e.get();                    if (key != null) {                        Object value = key.childValue(e.value);                        Entry c = new Entry(key, value);                        int h = key.threadLocalHashCode & (len - 1);                        while (table[h] != null)                            h = nextIndex(h, len);                        table[h] = c;                        size++;                    }                }            }        }    }}
阅读全文
0 0
原创粉丝点击