ThreadLocal类提供了线程局部变量。这些变量不同于他们的普通对应物,因为访问一个变量(通过get或set方法)的每个线程都有自己的局部变量,它独立于变量的初始化副本。ThreadLocal实例通常是类中的私有静态字段,他们希望将状态与某一个线程(例如,用户ID或事务ID)相关联。
简单说,ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,只要在本线程内,随时随地可以取,于是也就隔离了其他线程,主要解决多线程中数据数据因并发产生不一致问题,以此保证线程安全。
先来看一下ThreadLocal类的方法。
① initialValue()方法:返回该线程局部变量的初始值,是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。
protected T initialValue() {
return null;
}
②get()方法:返回当前线程所对应的线程局部变量。
/**
* Returns the value in the currentthread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initializedto the value returned
* by an invocation of the {@link#initialValue} method.
*
* @return the current thread's value ofthis thread-local
*/
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e =map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
③set()方法:设置当前线程的线程局部变量的值。
/**
* Returns the value in the currentthread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initializedto the value returned
* by an invocation of the {@link#initialValue} method.
*
* @return the current thread's value ofthis thread-local
*/
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e =map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
④remove()方法:将当前线程局部变量的值删除,目的是为了减少内存的占用
/**
* Removes the current thread's value forthis thread-local
* variable. If this thread-local variable is subsequently
* {@linkplain #get read} by the currentthread, its value will be
* reinitialized by invoking its {@link#initialValue} method,
* unless its value is {@linkplain #setset} by the current thread
* in the interim. This may result in multiple invocations ofthe
* <tt>initialValue</tt> methodin the current thread.
*
* @since 1.5
*/
public void remove() {
ThreadLocalMap m =getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
【实践】
ThreadLocal测试:
package com.bjpowernode.drp.util;
/**
* ThreadLocal测试
*
* @author happy
*
*/
public class TestThreadLocal {
// 通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值
private static ThreadLocal<Integer> threadNum = new ThreadLocal<Integer>() {
public Integer initialValue() {
return 0;
}
};
// 获取下一个序列值
public int getNextNum() {
threadNum.set(threadNum.get() + 1);
return threadNum.get();
}
private static class TestClient extends Thread {
private TestThreadLocal testThreadLocal;
public TestClient(TestThreadLocal testThreadLocal) {
this.testThreadLocal = testThreadLocal;
}
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("线程[" + Thread.currentThread().getName()
+ "] --> [" + testThreadLocal.getNextNum() + "]");
}
}
public static void main(String[] args) {
TestThreadLocal test = new TestThreadLocal();
// 创建 3个线程,共享testThreadLocal,但各自产生序列号
TestClient t1 = new TestClient(test);
TestClient t2 = new TestClient(test);
TestClient t3 = new TestClient(test);
t1.start();
t2.start();
t3.start();
}
}
}
drp中的实例:
不同的线程在使用ConnectionManager时,先判断connectionHolder.get()是否是null,如果是null,则说明当前线程还没有对应的Connection对象,这时创建一个Connection对象并添加到本地线程变量中;如果不为null,则说明当前的线程已经拥有了Connection对象,直接使用就可以了。这样,就保证了不同的线程使用线程相关的Connection,而不会使用其它线程的Connection。因此,这个connectionHolder.get()就可以做到在本线程内共享了。
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
* 采用ThreadLocal封装Connection
*
* @author Administrator
*
*/
public class ConnectionManager {
private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>();
/**
* 得到Connection
*
* @return
*/
public static Connection getConnection() {
// 先从线程变量中获取connection
Connection conn = connectionHolder.get();
// 如果在当前线程中没有绑定相应的Connection
if (conn == null) {
try {
JdbcConfig jdbcConfig = XmlConfigReader.getInstance()
.getJdbcConfig();
Class.forName(jdbcConfig.getDriverName());
conn = DriverManager.getConnection(jdbcConfig.getUrl(),
jdbcConfig.getUserName(), jdbcConfig.getPassword());
// 将Connection设置到ThreadLocal
connectionHolder.set(conn);
} catch (ClassNotFoundException e) {
e.printStackTrace();
throw new ApplicationException("系统错误,请联系系统管理员");
} catch (SQLException e) {
e.printStackTrace();
throw new ApplicationException("系统错误,请联系系统管理员");
}
}
return conn;
}
/**
* 关掉connection
*/
public static void closeConnection() {
Connection conn = connectionHolder.get();
if (conn != null) {
try {
conn.close();
// 从ThreadLocal中清除Connection
connectionHolder.remove();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
【比较】
ThreadLocal和Synchonized都用于解决多线程并发访问。synchronized是利用锁的机制,使变量或代码块在某一时该只能被一个线程访问。而ThreadLocal为每一个线程都提供了变量的副本,使得每个线程在某一时间访问到的是各自相应的对象,这样就隔离了多个线程对数据的数据共享。
Synchronized用于线程间的数据共享,而ThreadLocal则用于线程间的数据隔离。