ThreadLocal(一)设计ThreadLocal的目的
ThreadLoca(二)Looper中ThreadLocal的使用
先看看ThreadLocal类的文档注释:
/**
* This class provides thread-local variables. These variables differ from
* their normal counterparts in that each thread that accesses one (via its
* {@code get} or {@code set} method) has its own, independently initialized
* copy of the variable. {@code ThreadLocal} instances are typically private
* static fields in classes that wish to associate state with a thread (e.g.,
* a user ID or Transaction ID).
*
* <p>For example, the class below generates unique identifiers local to each
* thread.
* A thread's id is assigned the first time it invokes {@code ThreadId.get()}
* and remains unchanged on subsequent calls.
* <pre>
* 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);
*
* // Thread local variable containing each thread's ID
* private static final ThreadLocal<Integer> threadId =
* new ThreadLocal<Integer>() {
* @Override protected Integer initialValue() {
* return nextId.getAndIncrement();
* }
* };
*
* // Returns the current thread's unique ID, assigning it if necessary
* public static int get() {
* return threadId.get();
* }
* }
* </pre>
* <p>Each thread holds an implicit reference to its copy of a thread-local
* variable as long as the thread is alive and the {@code ThreadLocal}
* instance is accessible; after a thread goes away, all of its copies of
* thread-local instances are subject to garbage collection (unless other
* references to these copies exist).
*
翻译过来:
此类提供线程本地变量。这些变量不同于线程的普通对应变量,因为访问一个变量的每个线程(通过其get或set方法)都有自己的、独立初始化的变量副本。ThreadLocal实例通常是类中的private static 字段,这个字段希望将状态与线程(例如,用户ID或事务ID)关联的类中。
例如,下面的类为每个线程生成本地的唯一标识符。
一个线程的id, 在第一次调用时被分配,在随后的调用中保持不变。
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);
// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId =
new ThreadLocal<Integer>() {
@Override protected Integer initialValue() {
return nextId.getAndIncrement();
}
};
// Returns the current thread's unique ID, assigning it if necessary
public static int get() {
return threadId.get();
}
}
只要线程处于活动状态且ThreadLocal实例可访问,每个线程都持有对其线程局部变量副本的隐式引用;线程消失后,其线程本地实例的所有副本都将接受垃圾收集(除非存在对这些副本的其他引用)。
java程序中,某个线程的非私有成员变量是可以被其他线程访问和修改的。如下:
public class ThreadTest {
public static void main(String[] args) {
ThreadB b = new ThreadB();
new Thread(new Runnable() {
@Override
public void run() {
b.publicName ="pubThreadBBB";
b.protectedName = "proThreadBBB";
b.defaultName = "defThreadBBB";
System.out.println("将 ThreadB的名字修改为:" + b.defaultName);
}
}).start();
}
}
//新建一个ThreadB.java文件
public class ThreadB extends Thread {
public String publicName = "ThreadB";
protected String protectedName = "ThreadB";
String defaultName = "ThreadB";
private String privateName = "ThreadB";
}
如果想要某个线程私有的变量,不能被其他线程访问和修改,怎么办?
用private修饰或者设为默认,不就好了。
还真就是这样,我们看看Thread的源码:
public class Thread implements Runnable {
...
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
...
}
注释翻译过来就是:与此线程相关的ThreadLocal值。此映射由ThreadLocal类维护。所以:
设计ThreadLocal的目的,是为了存储线程私有的变量,其他线程无法访问,也就是实现了线程封闭。
如何防止其他线程,修改线程本地变量?
将ThreadB的写法,修改如下:
public class ThreadB extends Thread {
//ThreadLocal的注释中,建议用private static修饰
private static ThreadLocal<String> tl = new ThreadLocal<>();
public ThreadB(String name) {
super();
tl.set(name);
}
public String getNam() {
return tl.get(); //ThreadLocal内部有判空处理
}
}
这样,其他线程,就访问不能修改name变量了。
如果线程私有的变量有许多,其是不同类型,怎么办?
把多个变量,写在一个类里面,再把这个类作为ThreadLocal的泛型。例如:Android SDK中,Looper内部包含了MessageQueue,MessageQueue就是线程私有的。