模拟ThreadLocal功能实现
当前线程任意方法内操作连接对象
一个栈对应一个线程 , 一个方法调用另一个方法都是在一个线程内 , 只有执行了线程的start方法才会创建一个线程
定义一个Map集合 , key是当前线程(Thread.currentThread) , value是要绑定的数据(Connection对象)
- 以后获取,绑定,移除数据操作的key就是当前线程 , 当前线程是动态的 , 张三发起请求当前线程是t1线程 , 李四发送请求,当前线程是t2线程
ThreadLocal本质
自定义ThreadLocal类 , 将所有需要和当前线程绑定的数据要放到这个容器当中
public class MyThreadLocal<T> {
private Map<Thread, T> map = new HashMap<>();
//向ThreadLocal中当前线程绑定数据
public void set(T obj){
map.put(Thread.currentThread(), obj);
}
//从ThreadLocal中获取当前线程对应的数据
public T get(){
return map.get(Thread.currentThread());
}
//移除ThreadLocal当中当前线程对应的数据
public void remove(){
map.remove(Thread.currentThread());
}
}
DBUtil 工具类获取当前线程绑定的Connection对象
public class DBUtil {
// 静态变量特点:类加载时执行,并且只执行一次
// 全局的大Map集合
private static MyThreadLocal<Connection> local = new MyThreadLocal<>();
//每一次都调用这个方法来获取Connection对象
public static Connection getConnection(){
//获取当前线程对应的Connection对象
Connection connection = local.get();
if (connection == null) {
// 第一次调用:getConnection()方法的时候,connection一定是空的。空的就new一次。
connection = new Connection();
// 将new的Connection对象绑定到大Map集合中。
local.set(connection);
}
return connection;
}
}
//自定义Connection对象
public class Connection {
}
测试从Map集合中获取当前线程绑定的Connection对象
public class Test {
public static void main(String[] args) {
// 调用service
UserService userService = new UserService();
userService.save();
}
}
//UserService
public class UserService {
private UserDao userDao = new UserDao();
public void save(){
//这里获取的就是当前线程绑定的Connection对象
Connection connection = DBUtil.getConnection();
System.out.println(connection);
userDao.insert();
}
}
//UserDao
public class UserDao {
public void insert(){
//这里获取的就是当前线程绑定的Connection对象
Connection connection = DBUtil.getConnection();
System.out.println(connection);
System.out.println("User DAO insert");
}
}
使用java.lang.ThreadLocal类优化事务
java.lang.ThreadLocal类中里面有个Map集合 , 这个集合中的key是当前线程 , value是我们想要存储的对象(如存储Connection对象)
- 这个Map集合中的每个线程都有自己的Connection对象 , 需要的时候根据当前正在执行的线程获取对应的Connection对象
- 每次获取的Connection对象是从ThreadLocal中的Map集合中拿的 , 第一次获取的时候需要我们手动创建并添加到Map集合中
Connection对象关闭之后,要从大Map集合中移除
- Tomcat服务器是支持线程池的 , 线程池中有很多提前创建好的线程对象如t1 , t2 , t3 ,它们存在重复使用的问题
- 就是说一个人用过了t1线程,t1线程还有可能被其他用户使用 , 这时如果你不关闭 , 别人拿到的就是你已经关闭的Connection对象
ThreadLocal的常用方法
方法名 | 功能 |
---|---|
public Object get() | 获取Map集合中当前线程对应的对象 (如Connection对象) |
public void set(Object obj) | 把创建的对象存入Map集合中(如Connection对象) |
public void remove() | 删除Map集合中当前线程对应的对象(如Connection对象) |
使用ThreadLocal类优化事务
DBUtil⼯具类:将Connection对象存放到ThreadLocal里面的Map集合中, 保证service和dao中使⽤的Connection对象是同⼀个
public class DBUtil {
private static ResourceBundle bundle = ResourceBundle.getBundle("resources/jdbc");
private static String driver = bundle.getString("driver");
private static String url = bundle.getString("url");
private static String user = bundle.getString("user");
private static String password = bundle.getString("password");
// 工具类中的方法都是静态的,为了防止创建对象,故将构造方法私有化
private DBUtil(){}
// DBUtil类加载时注册驱动
static {
try {
Class.forName(driver);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
// 这个Map集合是全局的,在服务器中只有一个
private static ThreadLocal<Connection> local = new ThreadLocal<>();
//这里没有使用数据库连接池,直接创建连接对象
public static Connection getConnection() throws SQLException {
//获取当前线程对应的Connection对象,如果没有就创建并把它存入ThreadLocal类中的Map集合中
Connection conn = local.get();
if (conn == null) {
conn = DriverManager.getConnection(url, user, password);
local.set(conn);
}
return conn;
}
/**
* 关闭资源
* @param conn 连接对象
* @param stmt 数据库操作对象
* @param rs 结果集对象
*/
public static void close(Connection conn, Statement stmt, ResultSet rs){
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
if (conn != null) {
try {
// 根本原因是:Tomcat服务器是支持线程池的,也就是说一个人用过了t1线程,t1线程还有可能被其他用户使用
conn.close();
local.remove();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
}
不再接收service传过来的Connection对象 , 直接在AccountDao中获取当前线程对应的一个Connection对象
public class AccountDao {
//插入账户信息
public int insert(Account act) {
PreparedStatement ps = null;
int count = 0;
try {
//这里获取的Connection对象是从ThreadLocal中的Map集合中拿的(除了第一次是自己创建的)
Connection conn = DBUtil.getConnection();
//执行sql....
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
DBUtil.close(null, ps, null);
}
return count;
}
//根据主键删除账户
public int deleteById(Long id){
//执行sql...
}
//更新账户
public int update(Account act) {
//执行sql...
}
//根据账号查询账户
public Account selectByActno(String actno){
//执行sql...
}
//获取所有的账户
public List<Account> selectAll() {
//执行sql...
}
}