本文以数据库操作Dao为例进行描述ThreadLocal的使用,如下是一个反例:
package com.daxin.threadlocal.dao; import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement; public class UserDaoErrorDemo { // ①一个非线程安全的变量
private Connection conn; /**
* addUser在并发时候关于事务的提交容易出现错乱
*/
public void addUser() {
try { // ②引用非线程安全变量
conn.setAutoCommit(false);
Statement stat = conn.createStatement();
stat.executeQuery("sql");
conn.commit();
} catch (SQLException e) {
e.printStackTrace();
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
}
}
上面存在线程安全问题,在多个线程进行事务提交时候会出现错乱。因此可以通过如下方案:
package com.daxin.threadlocal.dao; import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement; /**
* 模拟数据库连接管理器
*
* @author liuguangxin
*
*/
class ConnectionManager { static String driverName = "";
static String url = "";
static String user = "";
static String password = ""; public static Connection getConnection() { try {
return DriverManager.getConnection(url, user, password);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
} }
/**
*
* @author liuguangxin
*
*/
public class UserDao { // ①使用ThreadLocal保存Connection变量
// 由于Dao通产刚在系统中是单例的,因此可以被多个线程共享,因此connThreadLocal也是被多个线程共享的。
private ThreadLocal<Connection> connThreadLocal = new ThreadLocal<Connection>(); public Connection getConnection() {
// ②如果connThreadLocal没有本线程对应的Connection创建一个新的Connection, 并将其保存到线程本地变量中。
if (connThreadLocal.get() == null) {
Connection conn = ConnectionManager.getConnection();
connThreadLocal.set(conn);
return conn;
} else {
// ③直接返回线程本地变量
return connThreadLocal.get();
}
} public void addUser(String userName, String passWord) { Connection conn = getConnection();
Statement stmt = null;
try {
conn.setAutoCommit(false);
stmt = conn.createStatement();
// 伪代码
stmt.executeQuery("insert into user ....");
conn.commit();
} catch (SQLException e) {
e.printStackTrace();
try {
conn.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
} finally {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
} } }