ThreadLocal的使用事例
2017-11-28 本文已影响42人
5e30faa7d323
列举几个使用ThreadLocal的事例,能更好的理解ThreadLocal。
1、运用ThreadLocal实现TransactionManager类
1. 写一个TransactionManager类:
/**
* 管理事务
*/
public class TransactionManager {
//使用ThreadLocal, 确保相同的线程获取到的是同一个连接.
private static ThreadLocal<Connection> local = new ThreadLocal<Connection>();
// 开启事务
public static void beginTransaction() throws SQLException {
Connection conn = JDBCUtils.getConnection();
conn.setAutoCommit(false);
// 将连接存入threadLocal
local.set(conn);
}
// 回滚事务
public static void rollback() throws SQLException {
Connection conn = local.get();
if (conn != null) {
conn.rollback();
conn.close();
// 清空threadLocal
local.remove();
}
}
// 提交事务
public static void commit() throws SQLException {
Connection conn = local.get();
if (conn != null) {
conn.commit();
// 清空threadLocal
local.remove();
}
}
// 关闭连接
public static void close() throws SQLException {
Connection conn = local.get();
if (conn != null) {
conn.close();
// 清空threadLocal
local.remove();
}
}
// 获取数据库连接
public static Connection getConnection() {
return local.get();
}
}
2、修改业务处理类
/**
* 业务逻辑层
*/
public class AccountService {
public void transfer(Account outAccount, Account inAccount, int money) throws SQLException {
// 开启 事务
TransactionManager.beginTransaction();
// 查询两个账户
AccountDAO accountDAO = new AccountDAO();
outAccount = accountDAO.findAccountById(outAccount.getId());
inAccount = accountDAO.findAccountById(inAccount.getId());
// 转账 - 修改原账户金额
outAccount.setMoney(outAccount.getMoney() - money);
inAccount.setMoney(inAccount.getMoney() + money);
try {
// 更新账户金额
accountDAO.update(outAccount);
accountDAO.update(inAccount);
// 转账成功, 提交事务
TransactionManager.commit();
} catch (Exception e) {
// 转账失败, 回滚事务
TransactionManager.rollback();
e.printStackTrace();
} finally {
// 关闭连接
TransactionManager.close();
}
}
}
3、Dao类
/**
* DAO层: CRUD
*/
public class AccountDAO {
// 查询账户
public Account findAccountById(int id) throws SQLException {
String sql = "select * from account where id = ?";
Object[] params = {id};
QueryRunner queryRunner = new QueryRunner(JDBCUtils.getDataSource());
return queryRunner.query(sql, new BeanHandler<Account>(Account.class), params);
}
// 更新账户
public void update(Account account) throws SQLException {
String sql = "update account set name = ?, money = ? where id = ?";
Object[] params = {account.getName(), account.getMoney(), account.getId()};
// 从threadLocal中获取连接, 同一个线程拿到的是同一个连接
Connection conn = TransactionManager.getConnection();
QueryRunner queryRunner = new QueryRunner();
queryRunner.update(conn, sql, params);
}
}
不需要传递Connection,直接从TransactionManager中获取连接。
service和dao都是通过TransactionManager来获取Connection, 同一个线程中, 它们在整个事务处理过程中使用了相同的Connection对象, 所以事务会处理成功, dao中没有接受和业务无关的对象, 消除了api污染, 另外使用TransactionManager来管理事务, 使service层的代码变简洁了.
2、工具类HibernateUtil运用ThreadLocal来管理Session
import org.hibernate.*;
import org.hibernate.cfg.*;
import org.hibernate.service.*;
import org.hibernate.boot.registry.*;
/**
* Description:
* @author VipMao
* @version 1.0
*/
/**
* 该工具类提供了一个属性:SessionFactory sessionFactory
* 并创建了sessionFactory 将它设置成static 这样其他程序就可以直接通过此工具类引用
* 提供了二个方法:
* 1:通过线程创建Session-->currentSession()
* 2:关闭Session-->closeSession()
* 需要在主类中手动关闭sessionFactory
*/
public class HibernateUtil
{
public static final SessionFactory sessionFactory;
//创建sessionFactory
static
{
try
{
// 采用默认的hibernate.cfg.xml来启动一个Configuration的实例
Configuration cfg = new Configuration()
.configure();
// 以Configuration实例来创建SessionFactory实例
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
.applySettings(cfg.getProperties()).build();
sessionFactory = cfg.buildSessionFactory(serviceRegistry);
}
catch (Throwable ex)
{
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
// ThreadLocal可以隔离多个线程的数据共享,因此不再需要对线程同步
public static final ThreadLocal<Session> session
= new ThreadLocal<Session>();
//创建Session
public static Session currentSession()
throws HibernateException
{
//通过线程对象.get()方法安全创建Session
Session s = session.get();
// 如果该线程还没有Session,则创建一个新的Session
if (s == null)
{
s = sessionFactory.openSession();
// 将获得的Session变量存储在ThreadLocal变量session里
session.set(s);
}
return s;
}
//关闭Session
public static void closeSession()
throws HibernateException
{
Session s = session.get();
if (s != null)
s.close();
session.set(null);
}
}
该工具类提供了一个属性:SessionFactory sessionFactory , 并将创建sessionFactory写在了static代码块里, 这样其他程序就可以直接通过HibernateUtil.sessionFactory引用,而不是再通过一系列的代码创建SessionFactory对象。
另外工具类还提供了二个方法:
1:通过线程创建Session-->currentSession()
工具类中通过ThreadLocal创建Session ,可以隔离多个线程的数据共享
2:关闭Session-->closeSession()
可以看出工具类将创建SessionFactory对象、Session对象以及关闭Session放在了不同的代码块以及方法里,这样以后咱们需要创建什么或者关闭什么直接通过HibernateUtil.XXX实现即可,大大缩减了代码量,提高代码复用性。