连接
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void rollbackAndClose() {
try {
Connection conn = getConnection();
//回滚事务
conn.rollback();
//解除绑定
tl.remove();
//释放连接
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
? (2) AccountService类的修改:不需要传递connection对象
package com.itheima.transfer.service;
import com.itheima.transfer.dao.AccountDao;
import com.itheima.transfer.utils.JdbcUtils;
import java.sql.Connection;
public class AccountService {
public boolean transfer(String outUser, String inUser, int money) {
AccountDao ad = new AccountDao();
try {
Connection conn = JdbcUtils.getConnection();
//开启事务
conn.setAutoCommit(false);
// 转出 : 这里不需要传参了 !
ad.out(outUser, money);
// 模拟转账过程中的异常
// int i = 1 / 0;
// 转入
ad.in(inUser, money);
//事务提交
JdbcUtils.commitAndClose();
} catch (Exception e) {
e.printStackTrace();
//事务回滚
JdbcUtils.rollbackAndClose();
return false;
}
return true;
}
}
? (3) AccountDao类的修改:照常使用
package com.itheima.transfer.dao;
import com.itheima.transfer.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class AccountDao {
public void out(String outUser, int money) throws SQLException {
String sql = "update account set money = money - ? where name = ?";
Connection conn = JdbcUtils.getConnection();
PreparedStatement pstm = conn.prepareStatement(sql);
pstm.setInt(1,money);
pstm.setString(2,outUser);
pstm.executeUpdate();
//照常使用
// JdbcUtils.release(pstm,conn);
JdbcUtils.release(pstm);
}
public void in(String inUser, int money) throws SQLException {
String sql = "update account set money = money + ? where name = ?";
Connection conn = JdbcUtils.getConnection();
PreparedStatement pstm = conn.prepareStatement(sql);
pstm.setInt(1,money);
pstm.setString(2,inUser);
pstm.executeUpdate();
// JdbcUtils.release(pstm,conn);
JdbcUtils.release(pstm);
}
}
2.3.2 ThreadLocal方案的好处
从上述的案例中我们可以看到, 在一些特定场景下,ThreadLocal方案有两个突出的优势:
-
传递数据 : 保存每个线程绑定的数据,在需要的地方可以直接获取, 避免参数直接传递带来的代码耦合问题
-
线程隔离 : 各线程之间的数据相互隔离却又具备并发性,避免同步方式带来的性能损失
3. ThreadLocal的内部结构
? 通过以上的学习,我们对ThreadLocal的作用有了一定的认识。现在我们一起来看一下ThreadLocal的内部结构,探究它能够实现线程数据隔离的原理。
3.1 常见的误解
? 通常,如果我们不去看源代码的话,我猜ThreadLocal
是这样子设计的:每个ThreadLocal
类都创建一个Map
,然后用线程的ID threadID
作为Map
的key
,要存储的局部变量作为Map
的value
,这样就能达到各个线程的局部变量隔离的效果。这是最简单的设计方法,JDK最早期的ThreadLocal
就是这样设计的。
3.2 核心结构
? 但是,JDK后面优化了设计方案,现时JDK8 ThreadLocal
的设计是:每个Thread
维护一个ThreadLocalMap
哈希表,这个哈希表的key
是ThreadLocal
实例本身,value
才是真正要存储的值Object
。
? (1) 每个Thread线程内部都有一个Map (ThreadLocalMap)
? (2) Map里面存储ThreadLocal对象(key)和线程的变量副本(value)
? (3)Thread内部的Map是由ThreadLocal维护的,由ThreadLocal负责向map获取和设置线程的变量值。
? (4)对于不同的线程,每次获取副本值时,别的线程并不能获取到当前线程的副本值,形成了副本的隔离,互不干扰。
3.3 这样设计的好处
? 这个设计与我们一开始说的设计刚好相反,这样设计有如下两个优势:
(1) 这样设计之后每个Map
存储的Entry
数量就会变少,因为之前的存储数量由Thread
的数量决定,现在是由ThreadLocal
的数量决定。
(2) 当Thread
销毁之后,对应的ThreadLocalMap
也会随之销毁,能减少内存的使用。
4. ThreadLocal的核心方法源码
? 基于ThreadLocal的内部结构,我们继续探究一下ThreadLocal的核心方法源码,更深入的了解其操作原理。
除了构造之外, ThreadLocal对外暴露的方法有以下4个:
方法声明 |
描述 |
protected T initialValue() |
返回当前线程局部变量的初始值 |
public void set( T value) |
设置当前线程绑定的局部变量 |
public T get() |
获取当前线程绑定的局部变量 |
public void remove() |
移除当前线程绑定的局部变量 |
其实get,set