ThreadLocal的接口方法:
- void set(Object value):设置当前线程的线程局部变量的值。
- public Object get():该方法返回当前线程所对应的线程局部变量。
- public void remove():将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。
需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
protected Object initialValue():返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。
在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal<T>。
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本。
下面是一个多线程向一个表插入数据的例子:
package com.lucky.concurrent;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class ThreadLocalDemo extends Thread {
@Override
public void run() {
ThradTask.executeUpdate();
}
public static void main(String[] args) {
for (int i = 0; i < 900; i++) {
ThreadLocalDemo demo = new ThreadLocalDemo();
demo.start();
}
}
}
class ThradTask {
static String sql = "insert into job_log(id, jobtype, logdate, status, ext1, ext2, ext3, log_time) values (seq_job_log.nextval, 1, sysdate, 1, null, null, null, sysdate)";
public static void executeUpdate() {
Connection con = null;
try {
con = DbUtil.getConnection();
con.setAutoCommit(false); // 关闭自动提交事务(开启事务)
con.prepareStatement(sql).executeUpdate();
con.commit();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DbUtil.closeConnection();
System.out.println("insert success ");
}
}
}
class DbUtil {
private static Connection con;// 静态连接对象,全部对象共享一个
public static Connection getConnection() {
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
con = DriverManager.getConnection(
"jdbc:oracle:thin:@xx.xx.xx.xx:1521:orcl", "dev_tc",
"123456");
} catch (Exception e) {
e.printStackTrace();
}
return con;
}
public static void closeConnection() {
if (con != null) {
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
看结果:

数据库关闭的连接,这个错误跟 Connection 有关系,那我就将主要精力放在检查 Connection 相关的代码上吧。是不是 Connection 不应该是 static 的呢?我当初设计成 static 的主要是为了让 DBUtil 的 static 方法访问起来更加方便,用 static 变量来存放 Connection 也提高了性能啊。怎么搞呢?接着就是重构了DbUtil。看下面的代码。只贴出DbUtil的代码。
class DbUtil {
private static ThreadLocal<Connection> local = new ThreadLocal<Connection>();
public static Connection getConnection() {
try {
Connection localCon = local.get();
if (localCon == null) {
Class.forName("oracle.jdbc.driver.OracleDriver");
localCon = DriverManager.getConnection(
"jdbc:oracle:thin:@xx.xx.xx.xx:1521:orcl", "dev_tc",
"123456");
local.set(localCon);
}
} catch (Exception e) {
//获取链接报错,比如超过最大连接数则销毁当前任务。<正常情况下是不能这样做>
Thread.currentThread().stop();
e.printStackTrace();
}
return local.get();
}
public static void closeConnection() {
Connection con = local.get();
if (con != null) {
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}finally{
local.remove();//将当前线程局部变量的值删除,目的是为了减少内存的占用
}
}
}
}
运行结果:

下面是另一个例子:
package test;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadLocalTest {
private static class Task implements Runnable {
private static ThreadLocal<Integer> number = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return 0;
}
};
public int getNumber() {
number.set(number.get() + 1);
return number.get();
}
@Override
public void run() {
try {
Thread.sleep(new Random().nextInt(5000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " "
+ getNumber());
}
}
public void run() {
ExecutorService pool = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
pool.execute(new ThreadLocalTest.Task());
}
pool.shutdown();
}
public static void main(String[] args) {
new ThreadLocalTest().run();
}
}
运行结果:每一个线程初始化值都是1。获得的结果就是static变量没有多线程共享。多线程安全。
此文章是学习了http://my.oschina.net/huangyong/blog/159489 这位大牛的而出世的。
下次继续写。先睡觉。