一 ThreadLocal 作用:
其主要作用是用来保存对应Thread需要存储的数据对象。一般情况下,通过ThreadLocal.set() 到线程中的对象是该线程自己使用的对象,其他线程是不需要访问的,也访问不到的。各个线程中访问的是不同的对象。
二 特点:
1.保证每个线程所保存的对象是唯一的;
2.ThreadLocal只能在对应的线程中(即在run()方法中)调用存储对象的方法【set(T value) 】,这样才能将数据对象与该线程绑定。;
3.ThreadLocal只能在对应的线程中(即在run()方法中)调用获取对象的方法【get()】,才能获取到为该线程保存的数据对象;
三 实例:
实例1 ,验证特点1

package com.example.handler;

import android.app.Activity;
import android.os.Bundle;
import android.widget.Button;

/**
 * @desc 主Activity
 * @creator caozhiqing
 * @data 2015/9/29
 */
public class MainActivity extends Activity {
    public String tag = ">>>>>>>>>>>>";

    public  ThreadLocal<Entity> local = new ThreadLocal<Entity>();

    protected Button button;
    protected Button run;

    public Entity entity1,entity2;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        button = (Button)findViewById(R.id.button);
        run = (Button)findViewById(R.id.run);

        entity1 = new Entity("entity1","1");
        entity2 = new Entity("entity2","2");
    }
}
package com.example.handler.threadLocal;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import com.example.handler.MainActivity;
import com.example.handler.R;

/**
 * @desc 请测试ThreadLocal
 * @creator caozhiqing
 * @data 2015/9/30
 */
public class ThreadLocalActivity extends MainActivity implements View.OnClickListener {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        button.setOnClickListener(this);
        run.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.button:
                break;
            case R.id.run:
                uniqueEntity();
                break;
            default: break;
        }
    }

    private void uniqueEntity(){
        Log.i(tag,"entity1:"+entity1);
        Log.i(tag,"entity2:"+entity2);
        new Thread(new MyThread()).start();
    }

    class MyThread implements  Runnable{
        @Override
        public void run() {
            //在同一线程中,把entity1、entity2都set进去,get到的只有entity2
            local.set(entity1);
            local.set(entity2);
            Log.i(tag,"entity :"+local.get());
        }
    }

}

日子分析:

android thread 网络请求 android threadlocal原理_Thread


由日志可以看出 entity2覆盖了entity1,被保存在ThreadLocal中。

实例2 ,验证特点2、3
特点2、3比较绕口,单代码展现比较简单且容易懂。

package com.example.handler.threadLocal;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import com.example.handler.MainActivity;
import com.example.handler.R;

/**
 * @desc 请测试ThreadLocal
 * @creator caozhiqing
 * @data 2015/9/30
 */
public class ThreadLocalActivity2 extends MainActivity implements View.OnClickListener {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        button.setOnClickListener(this);
        run.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.button:
                test1();
                break;
            case R.id.run:
                test2();
                break;
            default: break;
        }
    }

   private void test1(){

       Log.i(tag,"entity1:"+entity1);
       Log.i(tag,"entity2:"+entity2);
       local.remove();
       //在UI线程中把entity1放入ThreadLocal中
       local.set(entity1);
       //起新线程
       Thread thread1 = new Thread(new MyThread());
       thread1.start();
       Log.i(tag, "UI Thread entity:" + local.get());

//       09-30 11:38:31.247    6991-6991/com.example.handler I/>>>>>>>>>>>>﹕ entity1:com.example.handler.Entity@4195738
//       09-30 11:38:31.247    6991-6991/com.example.handler I/>>>>>>>>>>>>﹕ entity2:com.example.handler.Entity@41957418
//       09-30 11:38:31.247    6991-6991/com.example.handler I/>>>>>>>>>>>>﹕ UI Thread entity:com.example.handler.Entity@41957380
//       09-30 11:38:31.247    6991-7016/com.example.handler I/>>>>>>>>>>>>﹕ Other thread entity :null
//       在Thread1中没有取得entity1,这是因为entity1是在UI线程放入ThreadLocal;只能在UI本线程中取到
   }

    private void test2(){

        Log.i(tag,"entity1:"+entity1);
        Log.i(tag,"entity2:"+entity2);
        local.remove();
        //起新线程
        Thread thread2 = new Thread(new MyThread2());
        thread2.start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Log.i(tag, "UI Thread entity:" + local.get());


//        09-30 11:42:00.847    7140-7140/com.example.handler I/>>>>>>>>>>>>﹕ entity1:com.example.handler.Entity@41959090
//        09-30 11:42:00.847    7140-7140/com.example.handler I/>>>>>>>>>>>>﹕ entity2:com.example.handler.Entity@41959128
//        09-30 11:42:00.847    7140-7190/com.example.handler I/>>>>>>>>>>>>﹕ Other thread entity :com.example.handler.Entity@41959128
//        09-30 11:42:02.847    7140-7140/com.example.handler I/>>>>>>>>>>>>﹕ UI Thread entity:null
        //在线程thread2中把entity2存入ThreadLocal,再UI线程没能取到entity2,只在thread2中取到entity2
    }

    class MyThread implements  Runnable{
        @Override
        public void run() {
            //在新线程中看看是否能够取到entity1
             Log.i(tag,"Other thread entity :"+local.get());
        }
    }


    class MyThread2 implements  Runnable{
        @Override
        public void run() {
            //在新线程中讲entity2放入ThreadLocal

            local.set(entity2);
            Log.i(tag,"Other thread entity :"+local.get());
        }
    }
}

日志分析:

test1方法的日志如下

android thread 网络请求 android threadlocal原理_Thread_02


在Thread1中没有取得entity1,这是因为entity1是在UI线程放入ThreadLocal;只能在UI本线程中取到。test2方法的日志如下

android thread 网络请求 android threadlocal原理_android thread 网络请求_03

在线程thread2中把entity2存入ThreadLocal,再UI线程没能取到entity2,只在thread2中取到entity2

以上验证了特点2、3,说明ThreadLocal存储、获取数据都只能在同一线程,两个线程之间不能共享ThreadLocal中数据;每个线程能获取的数据只能是在本线程存入的数据,不能访问其他线程存入的数据。这保证的存入的数据只能在本线程中修改,起到加锁的作用。所以,ThreadLocal没有共享数据这一说。

四 源码分析:
在SDK中,创建线程时,会调用Thread类的create()方法,该方法代码如下:

/**
     * Initializes a new, existing Thread object with a runnable object,
     * the given name and belonging to the ThreadGroup passed as parameter.
     * This is the method that the several public constructors delegate their
     * work to.
     *
     * @param group ThreadGroup to which the new Thread will belong
     * @param runnable a java.lang.Runnable whose method <code>run</code> will
     *        be executed by the new Thread
     * @param threadName Name for the Thread being created
     * @param stackSize Platform dependent stack size
     * @throws IllegalThreadStateException if <code>group.destroy()</code> has
     *         already been done
     * @see java.lang.ThreadGroup
     * @see java.lang.Runnable
     */
    private void create(ThreadGroup group, Runnable runnable, String threadName, long stackSize) {
        Thread currentThread = Thread.currentThread();
        if (group == null) {
            group = currentThread.getThreadGroup();
        }

        if (group.isDestroyed()) {
            throw new IllegalThreadStateException("Group already destroyed");
        }

        this.group = group;

        synchronized (Thread.class) {
            id = ++Thread.count;
        }

        if (threadName == null) {
            this.name = "Thread-" + id;
        } else {
            this.name = threadName;
        }

        this.target = runnable;
        this.stackSize = stackSize;

        this.priority = currentThread.getPriority();

        this.contextClassLoader = currentThread.contextClassLoader;

        // Transfer over InheritableThreadLocals.
        if (currentThread.inheritableValues != null) {
            inheritableValues = new ThreadLocal.Values(currentThread.inheritableValues);
        }

        // add ourselves to our ThreadGroup of choice
        this.group.addThread(this);
    }

在这里创建了一个存储数据对象的容器ThreadLocal.Values,代码片段:

android thread 网络请求 android threadlocal原理_Thread_04

android thread 网络请求 android threadlocal原理_Thread_05

即当创建一个线程时,便为此线程创建了一个存放数据对象的容器(TheadLocal.Values)。在Values中,会根据当前的线程信息存储来存储数据对象,主要根据hashCode来区分。这样就保证了其存储的唯一性。有兴趣的可以看下Values的部分方法:

/**
         * Constructs a new, empty instance.
         */
        Values() {
            initializeTable(INITIAL_SIZE);
            this.size = 0;
            this.tombstones = 0;
        }

        /**
         * Used for InheritableThreadLocals.
         */
        Values(Values fromParent) {
            this.table = fromParent.table.clone();
            this.mask = fromParent.mask;
            this.size = fromParent.size;
            this.tombstones = fromParent.tombstones;
            this.maximumLoad = fromParent.maximumLoad;
            this.clean = fromParent.clean;
            inheritValues(fromParent);
        }

        /**
         * Inherits values from a parent thread.
         */
        @SuppressWarnings({"unchecked"})
        private void inheritValues(Values fromParent) {
            // Transfer values from parent to child thread.
            Object[] table = this.table;
            for (int i = table.length - 2; i >= 0; i -= 2) {
                Object k = table[i];

                if (k == null || k == TOMBSTONE) {
                    // Skip this entry.
                    continue;
                }

                // The table can only contain null, tombstones and references.
                Reference<InheritableThreadLocal<?>> reference
                        = (Reference<InheritableThreadLocal<?>>) k;
                // Raw type enables us to pass in an Object below.
                InheritableThreadLocal key = reference.get();
                if (key != null) {
                    // Replace value with filtered value.
                    // We should just let exceptions bubble out and tank
                    // the thread creation
                    table[i + 1] = key.childValue(fromParent.table[i + 1]);
                } else {
                    // The key was reclaimed.
                    table[i] = TOMBSTONE;
                    table[i + 1] = null;
                    fromParent.table[i] = TOMBSTONE;
                    fromParent.table[i + 1] = null;

                    tombstones++;
                    fromParent.tombstones++;

                    size--;
                    fromParent.size--;
                }
            }
        }
 /**
         * Sets entry for given ThreadLocal to given value, creating an
         * entry if necessary.
         */
        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;
                }
            }
        }

下面再看看ThreadLocal 如何利用Values来保存数据对象的。hreadLocal 中set()方法会去判断当前线程是否有与之对应的Values,如果没有就创建一个,再把数据对象存入Values;set方法的源码如下:

android thread 网络请求 android threadlocal原理_android_06

再来看看ThreadLocal获取数据对象的方法get();其源码如下:

android thread 网络请求 android threadlocal原理_线程_07

*总结:***ThreadLocal之所以能够做到为每条线程隔离数据,是他们不会共享数据,就是因为,在set方法是根据当前线程(Thread currentThread = Thread.currentThread();)来存储数据,同时get方法是根据当前线程来获取数据。

下篇将探讨一下Handler、Looper、MessageQueue三者之间的关系;理解ThreadLocal对正确分析以上三者的关系有着很重要的作用。
 以上仅是个人观点,望大家点评。