一 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());
}
}
}
日子分析:
由日志可以看出 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方法的日志如下
在Thread1中没有取得entity1,这是因为entity1是在UI线程放入ThreadLocal;只能在UI本线程中取到。test2方法的日志如下
在线程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,代码片段:
即当创建一个线程时,便为此线程创建了一个存放数据对象的容器(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方法的源码如下:
再来看看ThreadLocal获取数据对象的方法get();其源码如下:
*总结:***ThreadLocal之所以能够做到为每条线程隔离数据,是他们不会共享数据,就是因为,在set方法是根据当前线程(Thread currentThread = Thread.currentThread();)来存储数据,同时get方法是根据当前线程来获取数据。
下篇将探讨一下Handler、Looper、MessageQueue三者之间的关系;理解ThreadLocal对正确分析以上三者的关系有着很重要的作用。
以上仅是个人观点,望大家点评。