1、ThreadLocal的简介
ThreadLocal,即线程变量,是一个以ThreadLocal对象为键、任意对象为值的存储结构。这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。
可以通过set(T)方法来设置一个值,在当前线程下再通过get()方法获取到原先设置的值。
2、时间差的计算
在下面的例子中,构建了一个常用的Profiler类,它具有begin()和end()两个方法,而end()方法返回从begin()方法调用开始到end()方法被调用时的时间差,单位是毫秒。
public class Profiler {
private static ThreadLocal<Long> TIME_THREADLOCAL = new ThreadLocal<Long>();
public static final void begin() {
Long time = TIME_THREADLOCAL.get();
if (time == null) {
TIME_THREADLOCAL.set(System.currentTimeMillis());
}
}
public static final long end() {
return System.currentTimeMillis() - TIME_THREADLOCAL.get();
}
}
测试代码
public class Test {
public static void main(String[] args) {
Profiler.begin();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Cost: " + Profiler.end() + " mills");
}
}
3、线程范围内的数据共享,注意ThreadLocal的用法
// 线程内的共享数据,每次获取的实例都是和当前线程相关的
class MyThreadScopeData {
public MyThreadScopeData() {
}
// 利用ThreadLocal存储和线程相关的ThreadScopeData.java的实例
private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<MyThreadScopeData>();
// 用于获取ThreadScopeData.java和当前线程相关的实例
public static MyThreadScopeData getThreadInstance() {
MyThreadScopeData instance = map.get();
if (instance == null) {
instance = new MyThreadScopeData();
map.set(instance);
}
return instance;
}
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "ThreadScopeData [age=" + age + ", name=" + name + "]";
}
}
测试代码
public class ThreadScopeDataShare {
public static void main(String[] args) {
// 模拟线程
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
@Override
public void run() {
int data = new Random().nextInt();
MyThreadScopeData myThreadScopeData = MyThreadScopeData
.getThreadInstance();
myThreadScopeData.setAge(data);
myThreadScopeData.setName(data + "");
System.out.println(Thread.currentThread().getName()
+ " has put data is "
+ myThreadScopeData.toString());
// 模拟A模块使用数据
new A().get();
// 模拟B模块使用数据
new B().get();
}
}).start();
}
}
// A模块
static class A {
public void get() {
MyThreadScopeData myThreadScopeData = MyThreadScopeData
.getThreadInstance();
System.out.println("A from " + Thread.currentThread().getName()
+ " has put data is " + myThreadScopeData.toString());
}
}
// 模块
static class B {
public void get() {
MyThreadScopeData myThreadScopeData = MyThreadScopeData
.getThreadInstance();
System.out.println("B from " + Thread.currentThread().getName()
+ " has put data is " + myThreadScopeData.toString());
}
}
}
测试结果
Thread-0 has put data is ThreadScopeData [age=-1607661362, name=-1607661362]
Thread-1 has put data is ThreadScopeData [age=-715028589, name=-715028589]
A from Thread-0 has put data is ThreadScopeData [age=-1607661362, name=-1607661362]
A from Thread-1 has put data is ThreadScopeData [age=-715028589, name=-715028589]
B from Thread-0 has put data is ThreadScopeData [age=-1607661362, name=-1607661362]
B from Thread-1 has put data is ThreadScopeData [age=-715028589, name=-715028589]
4、利用ThreadLocal来保存之和当前线程有关的数据库连接
/**
* 数据库连接管理类
* @author 爽
*
*/
public class ConnectionManager {
/** 线程内共享Connection,ThreadLocal通常是全局的,支持泛型 */
private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();
public static Connection getCurrConnection() {
// 获取当前线程内共享的Connection
Connection conn = threadLocal.get();
try {
// 判断连接是否可用
if(conn == null || conn.isClosed()) {
// 创建新的Connection赋值给conn(略)
// 保存Connection
threadLocal.set(conn);
}
} catch (SQLException e) {
// 异常处理
}
return conn;
}
/**
* 关闭当前数据库连接
*/
public static void close() {
// 获取当前线程内共享的Connection
Connection conn = threadLocal.get();
try {
// 判断是否已经关闭
if(conn != null && !conn.isClosed()) {
// 关闭资源
conn.close();
// 移除Connection
threadLocal.remove();
conn = null;
}
} catch (SQLException e) {
// 异常处理
}
}
}
这样处理的好处:
- 统一管理Connection;
- 不需要显示传参Connection,代码更优雅;
- 降低耦合性。
// 模拟多线程的访问
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
@Override
public void run() {
}
}).start();
}