ThreadLocal 理解和简单使用场景

理解1 

ThreadLocal,很多地方叫做线程本地变量,也有些地方叫做线程本地存储,其实意思差不多。可能很多朋友都知道ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。

  这句话从字面上看起来很容易理解,但是真正理解并不是那么容易。

  我们还是先来看一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class  ConnectionManager {
     
     private  static  Connection connect =  null ;
     
     public  static  Connection openConnection() {
         if (connect ==  null ){
             connect = DriverManager.getConnection();
         }
         return  connect;
     }
     
     public  static  void  closeConnection() {
         if (connect!= null )
             connect.close();
     }
}

   假设有这样一个数据库链接管理类,这段代码在单线程中使用是没有任何问题的,但是如果在多线程中使用呢?很显然,在多线程中使用会存在线程安全问题:第一,这里面的2个方法都没有进行同步,很可能在openConnection方法中会多次创建connect;第二,由于connect是共享变量,那么必然在调用connect的地方需要使用到同步来保障线程安全,因为很可能一个线程在使用connect进行数据库操作,而另外一个线程调用closeConnection关闭链接。

用ThreadLocal 写法 :

1
2
3
4
5
6
7
8
9
10
private  static  ThreadLocal<Connection> connectionHolder
new  ThreadLocal<Connection>() {
public  Connection initialValue() {
     return  DriverManager.getConnection(DB_URL);
}
};
 
public  static  Connection getConnection() {
return  connectionHolder.get();
}


这样,如果多线程同时访问 ,着对 改线程来说,每个线程 都有自己的 Connection,这样 其他的connect 或者 Close, 操作的都是自己 Connection.


理解2

ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。我们自己就可以提供一个简单的实现版本:

public class SimpleThreadLocal {
    private Map valueMap = Collections.synchronizedMap(new HashMap());

    public void set(Object newValue) {
        valueMap.put(Thread.currentThread(), newValue); //①键为线程对象,值为本线程的变量副本
    }

    public Object get() {
        Thread currentThread = Thread.currentThread();

        Object o = valueMap.get(currentThread); //②返回本线程对应的变量

        if ((o == null) && !valueMap.containsKey(currentThread)) { //③如果在Map中不存在,放到Map中保存起来。
            o = initialValue();

            valueMap.put(currentThread, o);
        }

        return o;
    }

    public void remove() {
        valueMap.remove(Thread.currentThread());
    }

    public Object initialValue() {
        return null;
    }
}


下面的实例能够体现Spring对有状态Bean的改造思路:

代码清单3 TopicDao:非线程安全

public class TopicDao {

private Connection conn;一个非线程安全的变量

public void addTopic(){

Statement stat = conn.createStatement();引用非线程安全变量

}

}

由于处的conn是成员变量,因为addTopic()方法是非线程安全的,必须在使用时创建一个新TopicDao实例(非singleton)。下面使用ThreadLocalconn这个非线程安全的状态进行改造:

代码清单4 TopicDao:线程安全

import java.sql.Connection;

import java.sql.Statement;

public class TopicDao {

使用ThreadLocal保存Connection变量

private static ThreadLocal<Connection> connThreadLocal = new ThreadLocal<Connection>();

public static Connection getConnection(){

如果connThreadLocal没有本线程对应的Connection创建一个新的Connection

并将其保存到线程本地变量中。

if (connThreadLocal.get() == null) {

Connection conn = ConnectionManager.getConnection();

connThreadLocal.set(conn);

return conn;

}else{

return connThreadLocal.get();直接返回线程本地变量

}

}

public void addTopic() {

ThreadLocal中获取线程对应的Connection

Statement stat = getConnection().createStatement();

}

}



参考文章 

http://www.cnblogs.com/dolphin0520/p/3920407.html

http://justsee.iteye.com/blog/791919

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值