Android -理解ThreadLocal、Looper
2017-08-10 15:07
387 查看
一、理解Looper:消息循环
参考:android的消息处理机制(图+源码分析)——Looper,Handler,MessageAndroid消息循环是针对线程的(每个线程都可以有自己的消息队列和消息循环)。
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<Integer> threadId = * new ThreadLocal<Integer>() { * @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++; } } } } } }
相关文章推荐
- Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
- 深入理解Android消息处理系统——Looper、Handler、Thread
- Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
- Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
- Android异步消息处理机制 深入理解Looper、Handler、Message的关系
- Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
- Android基于Handler、Looper、MessageQueue、ThreadLocal的跨线程通信
- Android学习札记26:深入理解Android中的消息处理机制——Thread、Looper、MessageQueue和Handler(1)
- (转)深入理解Android消息处理系统——Looper、Handler、Thread
- 深入理解Android消息机制,从源码解析Handler,Looper,MessageQueue
- Android理解Looper、Handler、Message三者关系:
- Android Handler 异步消息处理机制二:源码解析,深入理解Looper、Handler、Message三者关系
- 深入理解Android消息处理系统——Looper、Handler、Thread
- Android中Looper之ThreadLocal
- Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关
- Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
- 深入理解Android消息处理系统——Looper、Handler、Thread
- 深入理解Android消息处理系统――Looper、Handler、Thread
- 深入理解Android消息处理系统——Looper、Handler、Thread
- Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系