ThreadLocal功能实现

模拟ThreadLocal功能实现

当前线程任意方法内操作连接对象

一个栈对应一个线程 , 一个方法调用另一个方法都是在一个线程内 , 只有执行了线程的start方法才会创建一个线程

定义一个Map集合 , key是当前线程(Thread.currentThread) , value是要绑定的数据(Connection对象)

  • 以后获取,绑定,移除数据操作的key就是当前线程 , 当前线程是动态的 , 张三发起请求当前线程是t1线程 , 李四发送请求,当前线程是t2线程

ThreadLocal功能实现_第1张图片

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...
    }
}

你可能感兴趣的:(JavaWeb,java,开发语言)