ThreadLocal 在jdbc事务的应用
ThreadLocal
优势:
- 传递数据:在需要的地方获取数据,避免参数传递带来的代码耦合问题
- 线程隔离:在各线程的数据相互隔离又能并发,避免了同步方式带来的性能损失
基本使用
public class Test {
ThreadLocal<String> t1 = new ThreadLocal<>();
public String getContent() {
return t1.get();
}
public void setContent(String content) {
t1.set(content);
}
}
public class AccountService {
public boolean transfer(String out, String in, int money) {
AccountDao dao = new AccountDao();
Connection conn = null;
try {
synchronized (AccountService.class) {
conn = JdbcUtil.getConnection();
// 关闭自动提交,开启事务
conn.setAutoCommit(false);
/* 事务使用注意点: 1. 保证两次数据库操作的conn对象一致 2. 线程隔离 常规解决方案: 1. 传参:service层的conn传到dao层 2. 加锁(别的线程不能在此时getConn,否则可能导致当前线程的conn发生改变) 常规解决方案的弊端: 1. 提高代码耦合度 2. 降低程序性能 */
dao.out(out, money, conn);
dao.in(in, money, conn);
conn.commit();
conn.close();
return true;
}
} catch (Exception e) {
conn.rollback();
conn.close();
return false;
}
}
}
/** * 1. 方法添加参数 conn,不能从连接池获取conn * 2. dao层不能释放连接 */
public class AccountDao {
// 转出
public void out(String out, int money, Connection conn) {
String sql = "...";
PreparedStatement pstm = conn.prepareStatement(sql);
// ...
pstm.executeUpdate();
}
// 转入
public void in(String in, int money, Connection conn) {
String sql = "...";
PreparedStatement pstm = conn.prepareStatement(sql);
// ...
pstm.executeUpdate();
}
}
改进
public class JdbcUtils {
ThreadLocal<Connection> threadLocal = new ThreadLocal();
/* 原来:直接从数据池中获取连接 现在:1. 直接获取当前线程绑定的conn 2. 如果连接对象是空的再去连接池中获取conn 在将此conn跟当前对象绑定 */
public static Connection getConnection () {
Connection conn = threadLocal.get();
if (conn == null) {
conn = database.getConnection();
threadLocal.set(conn);
}
return conn;
}
public static void commitAndClose() {
conn.commit();
// 解绑conn
threadLocal.remove();
conn.close();
}
public static void rollbackAndClose() {
conn.rollback();
// 解绑conn
threadLocal.remove();
conn.close();
}
}
public class AccountService {
public boolean transfer(String out, String in, int money) {
AccountDao dao = new AccountDao();
Connection conn = null;
try {
synchronized (AccountService.class) {
// 从数据库获取conn并存入当前线程
// 其他线程到来会从连接池获取新conn
conn = JdbcUtils.getConnection();
conn.setAutoCommit(false);
dao.out(out, money, conn);
dao.in(in, money, conn);
JdbcUtils.commitAndClose();
return true;
}
} catch (Exception e) {
JdbcUtils.rollbackAndClose();
return false;
}
}
}
public class AccountDao {
// 转出
public void out(String out, int money) {
// 从当前线程获取conn
Connection conn = JdbcUtils.getConnection();
String sql = "...";
PreparedStatement pstm = conn.prepareStatement(sql);
// ...
pstm.executeUpdate();
}
// 转入
public void in(String in, int money) {
// 从当前线程获取conn
Connection conn = JdbcUtils.getConnection();
String sql = "...";
PreparedStatement pstm = conn.prepareStatement(sql);
// ...
pstm.executeUpdate();
}
}
ThreadLocal源码
public class ThreadLocal {
public void set(T value) {
ThreadLocalMap map =
Thread.currentThread().threadLocals;
if (map != null) {
map.set(this, value);
}
}
public T get() {
ThreadLocalMap map =
Thread.currentThread().threadLocals;
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
T result = e.value;
return result;
}
}
}
public void remove() {
ThreadLocalMap map =
Thread.currentThread().threadLocals;
if (map != null) {
m.remove(this);
}
}
}