前言
我們的項(xiàng)目存在大量用戶同時(shí)訪問的情況,那么就會(huì)出現(xiàn)大量線程并發(fā)訪問數(shù)據(jù)庫(kù),這樣會(huì)帶來線程同步問題,本章我們將討論MyBatis的線程同步問題和優(yōu)化方法。
MyBatis的線程同步問題
MyBatis需要通過SqlSession實(shí)現(xiàn)數(shù)據(jù)庫(kù)操作,而SQLSession內(nèi)部的實(shí)現(xiàn)需要使用JDBC的Connection連接對(duì)象,而Connection對(duì)象是非線程安全的,當(dāng)多個(gè)線程同時(shí)訪問時(shí),就可能出現(xiàn)線程同步的問題。
線程同步的解決方法
我們前面學(xué)習(xí)過解決線程同步的方法是:鎖機(jī)制。
我們可以給所有數(shù)據(jù)庫(kù)相關(guān)方法或代碼添加synchronized關(guān)鍵字,本質(zhì)上是讓所有線程排隊(duì)執(zhí)行這些操作。
這樣解決了線程同步問題,但是會(huì)帶來執(zhí)行效率的降低,如果大量的用戶訪問時(shí)會(huì)導(dǎo)致長(zhǎng)時(shí)間的等待,所以今天我們將學(xué)習(xí)另一種解決方法。
ThreadLocal解決線程同步
ThreadLocal(線程局部變量),可以為每個(gè)線程創(chuàng)建對(duì)象的副本,這樣就不存在多線程訪問一個(gè)對(duì)象的情況,以空間換時(shí)間,效率高,速度快,但是內(nèi)存空間消耗更大。
ThreadLocal的用法
創(chuàng)建方法:
ThreadLocal<數(shù)據(jù)的類型> threadLocal = new ThreadLocal<數(shù)據(jù)類型>();
數(shù)據(jù)的類型是每個(gè)線程中需要保存數(shù)據(jù)的類型。
數(shù)據(jù)存取:
void set(Object 數(shù)據(jù)) 將數(shù)據(jù)和當(dāng)前線程綁定起來。
Object get() 從當(dāng)前線程中獲得綁定的數(shù)據(jù)。
Session線程安全的優(yōu)化方法
1)創(chuàng)建ThreadLocal來保存SqlSession
2)編寫獲得SqlSession的方法
1.調(diào)用ThreadLocal的get方法來獲得SqlSession
2.如果SqlSession對(duì)象為null,調(diào)用工廠來創(chuàng)建SqlSession,使用ThreadLocal的set方法保存到線程中,返回SqlSession對(duì)象
3.如果SqlSession對(duì)象不為null,就直接返回
示例代碼:
/**
* MyBatis工具類
* 用于獲得當(dāng)前線程中的SqlSession
* 使用ThreadLocal解決線程安全問題
*/
public class MyBatisUtils {
public static final String CONFIG_FILENAME = "mybatis-config.xml";
//使用ThreadLocal保存SQLSession對(duì)象
private static ThreadLocal threadLocal = new ThreadLocal();
//SqlSession的工廠,單例
private static SqlSessionFactory factory = null;
/**
* 創(chuàng)建工廠
*/
public static void buildFactory(){
try {
factory = new SqlSessionFactoryBuilder().build(
Resources.getResourceAsStream(CONFIG_FILENAME));
} catch (IOException e) {
e.printStackTrace();
}
}
//在靜態(tài)代碼塊中執(zhí)行,保證工廠的創(chuàng)建只執(zhí)行一次
static{
buildFactory();
}
/**
* 從當(dāng)前線程中獲得Session
* @return
*/
public static SqlSession getSession(){
//從ThreadLocal獲得線程中的SqlSession
SqlSession sqlSession = threadLocal.get();
if(sqlSession == null){
//如果SqlSession為空,創(chuàng)建SqlSession
if(factory == null){
buildFactory();
}
sqlSession = factory.openSession();
//把新創(chuàng)建的SqlSession,存入到ThreadLocal,綁定到線程中
threadLocal.set(sqlSession);
}
return sqlSession;
}
/**
* 關(guān)閉Session
*/
public static void closeSession(){
//從ThreadLocal獲得線程中的SqlSession
SqlSession sqlSession = threadLocal.get();
if(sqlSession != null){
//關(guān)閉會(huì)話
sqlSession.close();
//設(shè)置為null,gc會(huì)盡快回收
sqlSession = null;
//刪除掉ThreadLocal中的SqlSession
threadLocal.set(null);
}
}
}
總結(jié)
線程同步是進(jìn)行JavaEE開發(fā)需要重點(diǎn)考慮的問題,MyBatis的SQLSession有線程同步問題,使用ThreadLocal為每個(gè)線程綁定自己的SQLSession副本,可以解決線程同步問題,同時(shí)不會(huì)降低程序執(zhí)行效率。