問題場景一:高併發的時候,我們在調用一些公有的對象資源的時候,會有線程安全問題。
解決線程安全問題
方法一:對全局變量進行加鎖。缺點是其他線程要使用的話,就必須等待,耗時。
方法二:把全局變量變成局部方法變量。這樣就解決了線程安全問題。
問題場景二:我既要解決安全問題,我又要實現全局共享呢。
很多地方我們都需要這樣做。比如:我們需要獲取數據庫連接的時候,我們會單獨封裝一個方法叫getConnection()這個方法用來獲取connect
,那麼我們還會封裝幾個方法
commit()提交事物,
rollback()回滾事物的方法,
closeConnection()關閉連接方法。
這幾個方法自然需要共用一個connection對象才能對當前連接進行操作。
那麼如何共用呢?全局變量會造成線程安全問題,加鎖會對性能有影響,設置爲局部變量,那麼就不能在各個方法中使用了。 我們現在需要一個:既能夠共用,但又不是全局變量線程安全的東西,彷彿是局部定義的變量能夠在各個方法中重用一樣。
這個時候就可以使用ThreadLocal來解決既能夠共用,又不會造成線程安全的問題。**
至於ThreadLocal怎麼用,網上很多例子,我也看了,但是看來看去都沒說爲什麼要用,只說了怎麼用,怎麼用的博客太多了。我這裏就簡單的貼一下代碼案例:
package day02;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import org.apache.commons.dbcp.BasicDataSource;
/**
* 該類用於維護數據庫連接
* @author tarena
*
*/
public class DBUtil {
/*
* ThreadLocal用於線程跨方法共享數據使用
* TheaddLocal內部有一個Map,key爲要共
* 享數據的線程本身,value就是其需要共享的數據。
*/
private static ThreadLocal<Connection> tl;
private static BasicDataSource dataSource;
private static String className;
private static String url;
private static String username;
private static String password;
static{
tl = new ThreadLocal<Connection>();
/*
* properties文件
* properties文件常被用來做配置文件,
* 結構簡單。
*/
try{
Properties pro = new Properties();
pro.load(new FileInputStream("src/main/java/day02/config.properties"));
className = pro.getProperty("classname");
url = pro.getProperty("url");
username = pro.getProperty("username");
password = pro.getProperty("password");
int maxActive = Integer.parseInt(pro.getProperty("maxactive"));
long maxWait = Long.parseLong(pro.getProperty("maxwait"));
//初始化連接池
dataSource = new BasicDataSource();
dataSource.setDriverClassName(className);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
//設置最大連接
dataSource.setMaxActive(maxActive);
//設置最大等待時間
dataSource.setMaxWait(maxWait);
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 獲取數據庫連接
* @return
* @throws SQLException
* @throws ClassNotFoundException
*/
public static Connection getConnection() throws SQLException, ClassNotFoundException{
Connection connection = dataSource.getConnection();
// Class.forName(className);
// Connection connection = DriverManager.getConnection(url,username,password);
tl.set(connection);
return connection;
}
/**
* 關閉數據庫
* @param conn
*/
public static void closeConnection(){
try{
//關閉連接前,提交事務
TransCommit();
//如果是使用連接池獲得的連接,那麼調用該連接的close方法不會關閉連接,只是表示當前連接使用完畢,空閒。
Connection connection = tl.get();
//刪除當前線程所存放的連接
tl.remove();
if(connection != null){
connection.close();
}
}catch(Exception e){
e.printStackTrace();
}
}
/*
* 開啓事務
*/
public static void TransBegin(){
try {
tl.get().setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
//回滾事務
public static void TransRollBack(){
try {
tl.get().rollback();
tl.get().setAutoCommit(true);
} catch (SQLException e) {
e.printStackTrace();
}
}
//提交事務
public static void TransCommit(){
try {
tl.get().commit();
tl.get().setAutoCommit(true);
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}