ThreadLocal是为解决多线程程序的并发问题而提出的,可以称之为线程局部变量。与一般的变量的区别在于,生命周期是在线程范围内的。
static变量是的生命周期与类的使用周期相同,即只要类存在,那么static变量也就存在。
那么一个 static 的 ThreadLocal会是什么样的呢?
看下面一个例子,
public class SequenceNumber {
private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>(){
public Integer initialValue(){
return 0;
}
};
public int getNextNum(){
seqNum.set(seqNum.get() + 1);
return seqNum.get();
}
public static void main(String[] args){
SequenceNumber sn = new SequenceNumber();
TestClient t1 = new TestClient(sn);
TestClient t2 = new TestClient(sn);
TestClient t3 = new TestClient(sn);
t1.start();
t2.start();
t3.start();
t1.print();
t2.print();
t3.print();
}
private static class TestClient extends Thread{
private SequenceNumber sn ;
public TestClient(SequenceNumber sn ){
this.sn = sn;
}
public void run(){
for(int i=0; i< 3; i++){
System.out.println( Thread.currentThread().getName() + " --> " + sn.getNextNum());
}
}
public void print(){
for(int i=0; i< 3; i++){
System.out.println( Thread.currentThread().getName() + " --> " + sn.getNextNum());
}
}
}
}
下面是结果
Thread-2 --> 1
Thread-2 --> 2
Thread-2 --> 3
Thread-0 --> 1
Thread-0 --> 2
Thread-0 --> 3
Thread-1 --> 1
Thread-1 --> 2
Thread-1 --> 3
main --> 1
main --> 2
main --> 3
main --> 4
main --> 5
main --> 6
main --> 7
main --> 8
main --> 9
可以发现,static的ThreadLocal变量是一个与线程相关的静态变量,即一个线程内,static变量是被各个实例共同引用的,但是不同线程内,static变量是隔开的。
下面的实例能够体现Spring对有状态Bean的改造思路:
代码清单3 TopicDao:
// 非线程安全
public class TopicDao {
// 一个非线程安全的变量
private Connection conn;
public void addTopic(){
// 引用非线程安全变量
Statement stat = conn.createStatement();
…
}
}
由于1处的conn是成员变量,因为addTopic()方法是非线程安全的,必须在使用时创建一个新TopicDao实例(非singleton)。
下面使用ThreadLocal对conn这个非线程安全的“状态”进行改造:
代码清单4 TopicDao:
// 线程安全
package threadLocalDemo;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
public class SqlConnection {
// 1. 使用ThreadLocal保存Connection变量
privatestatic ThreadLocal<Connection>connThreadLocal = newThreadLocal<Connection>();
publicstatic Connection getConnection() {
// 2. 如果connThreadLocal没有本线程对应的Connection创建一个新的Connection,
// 并将其保存到线程本地变量中。
if (connThreadLocal.get() == null) {
Connection conn = getConnection();
connThreadLocal.set(conn);
return conn;
} else {
return connThreadLocal.get();
// 3. 直接返回线程本地变量
}
}
public voidaddTopic() {
// 4. 从ThreadLocal中获取线程对应的Connection
try {
Statement stat = getConnection().createStatement();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
为何通常将ThreadLocal变量设置为static
理由:
为了避免重复创建TSO(thread specific object,即与线程相关的变量)。
需要注意的是:无法解决共享对象的更新问题。(引用于《阿里巴巴JAVA开发规范》)
static定义的类变量本来是可以进行变量共享的,但是因为ThreadLocal根除了对变量的共享,所以static Thread< xxx> object无法实现类的共享和同步更新。
分析
一个ThreadLocal实例对应当前线程中的一个TSO实例。因此,如果把ThreadLocal声明为某个类的实例变量(而不是静态变量),那么每创建一个该类的实例就会导致一个新的TSO实例被创建。显然,这些被创建的TSO实例是同一个类的实例。于是,同一个线程可能会访问到同一个TSO(指类)的不同实例,这即便不会导致错误,也会导致浪费(重复创建等同的对象)!因此,一般我们将ThreadLocal使用static修饰即可。
由于ThreadLocal是某个类的一个静态变量。因此,只要相应的类没有被垃圾回收掉,那么这个类就会持有对相应ThreadLocal实例的引用。
什么是ThreadLocal?
java.lang.ThreadLocal类实现了线程的本地存储,我们可以用该类来创建和管理线程。
ThreadLocal的内部实现:
ThreadLocal的内部实现包括一个类似HashMap的对象,这里称之为ThreadLocalMap。
ThreadLocalMap的key会持有对ThreadLocal实例的弱引用(Weak Reference),value会引用TSO实例。
具体的可以参考:彻底理解ThreadLocal
ThreadLocal的使用:
《thinking in java 第四版》p690
来源:oschina
链接:https://my.oschina.net/u/4316562/blog/4278609