多线程安全之ThreadLocal
使用场景
每个ThreadLocal可以放一个线程级别的变量,但是它本身可以被多个线程共享使用,而且又可以达到线程安全的目的,且绝对线程安全。
Demo
public class ThreadLocalDemo {
static class ResourceClass {
public static ThreadLocal sTHREADLOCAL_1 = new ThreadLocal();
public static String sNoThreadLocal;
}
static classThreadLocalSetClass {
public void setOne(String value) {
ResourceClass.sTHREADLOCAL_1.set(value);
}
public void setNoThreadLocalValue(String value) {
ResourceClass.sNoThreadLocal = value;
}
}
static classThreadLocalGetClass {
public void display() {
System.out.println(Thread.currentThread().getName() +": ThreadLocal "
+ ResourceClass.sTHREADLOCAL_1.get() + ", NOThreadLocal "
+ ResourceClass.sNoThreadLocal);
}
}
public static void main(String[] args) {
final ThreadLocalSetClass threadLocalSetClass = new ThreadLocalSetClass();
final ThreadLocalGetClass threadLocalGetClass = new ThreadLocalGetClass();
for (int i = 0; i < 15; i++) {
final String resouce1 = "Vlaue = (" + i + ")";
Threadthread = new Thread() {
public void run() {
try {
threadLocalSetClass.setOne(resouce1);
threadLocalSetClass.setNoThreadLocalValue(resouce1);
threadLocalGetClass.display();
}finally {
ResourceClass.sTHREADLOCAL_1.remove();
}
}
};
thread.setName("Thread - " + i);
thread.start();
}
}
}
输出结果
Thread - 11: ThreadLocal Vlaue = (11),NOThreadLocal Vlaue = (12)
Thread - 10: ThreadLocal Vlaue = (10),NOThreadLocal Vlaue = (12)
Thread - 9: ThreadLocal Vlaue = (9),NOThreadLocal Vlaue = (1)
Thread - 1: ThreadLocal Vlaue = (1),NOThreadLocal Vlaue = (1)
Thread - 3: ThreadLocal Vlaue = (3),NOThreadLocal Vlaue = (2)
Thread - 2: ThreadLocal Vlaue = (2),NOThreadLocal Vlaue = (1)
Thread - 4: ThreadLocal Vlaue = (4),NOThreadLocal Vlaue = (4)
Thread - 5: ThreadLocal Vlaue = (5),NOThreadLocal Vlaue = (4)
Thread - 6: ThreadLocal Vlaue = (6),NOThreadLocal Vlaue = (6)
Thread - 14: ThreadLocal Vlaue = (14),NOThreadLocal Vlaue = (14)
Thread - 13: ThreadLocal Vlaue = (13),NOThreadLocal Vlaue = (14)
Thread - 0: ThreadLocal Vlaue = (0),NOThreadLocal Vlaue = (1)
Thread - 8: ThreadLocal Vlaue = (8),NOThreadLocal Vlaue = (1)
Thread - 7: ThreadLocal Vlaue = (7),NOThreadLocal Vlaue = (12)
Thread - 12: ThreadLocal Vlaue = (12),NOThreadLocal Vlaue = (12)
注意:根据Thread的启动时间,每次打印出的线程顺序会有不同
结果分析
使用了ThreadLocal定义的变量每次使用的i值和线程命名时所使用的i值一样,而不使用ThreadLocal定义的变量则i值不确定。可验证ThreadLocal做到了线程安全。
原理分析
针对Android 6.0的ThreadLocal进行分析:
示例中使用了set(),get(),remove()方法,接下来针对set()进行分析:
Thread currentThread=Thread.currentThread();
Values values=values(currentThread);
values=initializeValues(currentThread);
}
}
Values values(Thread current){
return current.localValues;
}
/**
* Sets entry forgiven ThreadLocal to given value, creating an
* entry ifnecessary.
*/
void put(Thread Localkey,Object value){
cleanUp();
// Keep track offirst tombstone. That's where we want to go back
// and add anentry if necessary.
int firstTombstone= -1;
for (int index=key.hash&mask;;index=next(index)){
//Replace existing entry.
return;
}
if (k ==null) {
if (firstTombstone== -1) {
//Fill in null slot.
size++;
return;
}
// Goback and replace first tombstone.
table[firstTombstone]=key.reference;
table[firstTombstone+ 1] =value;
tombstones--;
size++;
return;
}
// Rememberfirst tombstone.
if (firstTombstone== -1 && k ==TOMBSTONE){
}
}
}
File: libcore/luni/src/main/java/java/lang/ThreadLocal.java
可简单理解为:每个Thread对象都有一个ThreadLocal.Vlaues的对像。而ThreadLocal就是对通过每个Thread自身的ThreadLocal.Vlaues对像进行值的保存和读取。相当于每个线程有自己的数据。所以可以做到线程安全。
延生
InheritableThreadLocal
扩展了 ThreadLocal,为子线程提供从父线程那里继承的值。
protected T childValue(T parentValue){
return parentValue;
}
return current.inheritableValues;
}
ValuesinitializeValues(Threadcurrent){
return current.inheritableValues= new Values();
}
File:libcore/luni/src/main/java/java/lang/InheritableThreadLocal.java
InheritableThreadLocal.java这个类只是重写了ThreadLocal的几个方法,那他是怎么做到继承父线程的值呢?重点就在Thread.java是怎么使用inheritableValues这个变量了。
ThreadLocal.Values inheritableValues;
public Thread() {
}
private void create(ThreadGroup group,Runnable runnable,String threadName,long stackSize){
…
if(currentThread.inheritableValues!=null){
inheritableValues=new ThreadLocal.Values(currentThread.inheritableValues);
}
…
}
File:libcore/libart/src/main/java/java/lang/Thread.java
在Thread的创建时,会把inheritableValues从当前线程同步给子线程。