【java項目實戰】ThreadLocal封裝Connection,實現同一線程共享資源

 線程安全一直是程序猿們關注的焦點,多線程也一直是比較讓人頭疼的話題,想必大家曾經也遇到過各種各種的問題,我就不再累述了。當然,解決方式也有很多,這篇博文給大家提供一種很好的解決線程安全問題的思路。

 

      首先,我們先簡單的認識一下ThreadLocal,之後是實例+解析,最後一句話總結。


1、認識一下ThreaLocal


       認識ThreadLocal必須要通過api文檔,不僅僅具有說服力,而且它會給你更加全面的解釋。下面我我給大家從api文檔上截取一張圖,並標出來了七點需要重點理解的內容,實例過後的解析也是重點解釋這七部分。




      對於上面的內容,不理解沒有關係,我們通過下面的實例加深一下理解,實例之後我會給大家一個更加深入的解釋。


2、ThreaLocal封裝Connection實例+解析


       下面的代碼只是ThreaLocal封裝Connection的核心代碼,對於多餘的內容成功避開就好,並且有一部分代碼是“dom4j解析xml文件,連接數據庫”的內容,非常適合初學者,如有需要,請您移駕到此


[java] view plain copy
 print?
  1. package com.bjpowernode.drp.util;  
  2.   
  3. import java.sql.Connection;  
  4. import java.sql.DriverManager;  
  5. import java.sql.ResultSet;  
  6. import java.sql.SQLException;  
  7. import java.sql.Statement;  
  8.   
  9. /** 
  10.  * 採用ThreadLocal封裝Connection 
  11.  * 只要線程是活動的,沒有結束,ThreadLocal是可訪問的,就可以訪問本線程的connection 
  12.  *  
  13.  * @author liang 
  14.  * 
  15.  */  
  16. public class ConnectionManager {  
  17.   
  18.     //使用ThreadLocal保存Connection變量  
  19.     private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>();  
  20.       
  21.     /** 
  22.      * 連接Connection 
  23.      * @return 
  24.      */  
  25.     public static Connection getConnection(){  
  26.         //ThreadLocal取得當前線程的connection  
  27.         Connection conn = connectionHolder.get();  
  28.         //如果ThreadLocal沒有綁定相應的Connection,創建一個新的Connection,  
  29.         //並將其保存到本地線程變量中。  
  30.         if(conn == null){  
  31.             try {  
  32.                 JdbcConfig jdbcConfig = XmlConfigReader.getInstance().getJdbcConfig();  
  33.                 Class.forName(jdbcConfig.getDriverName());                
  34.                 conn = DriverManager.getConnection(jdbcConfig.getUrl(), jdbcConfig.getUserName(), jdbcConfig.getPassword());  
  35.                 //將當前線程的Connection設置到ThreadLocal  
  36.                 connectionHolder.set(conn);  
  37.             } catch (ClassNotFoundException e) {  
  38.                 e.printStackTrace();  
  39.                 throw new ApplicationException("系統錯誤,請聯繫系統管理員");  
  40.             } catch (SQLException e) {  
  41.                 e.printStackTrace();  
  42.             throw new ApplicationException("系統錯誤,請聯繫系統管理員");  
  43.             }  
  44.         }  
  45.         return conn;                                      
  46.           
  47.     }  
  48.     /** 
  49.      * 關閉Connection,清除集合中的Connection 
  50.      */  
  51.     public static void closeConnection(){  
  52.         //ThreadLocal取得當前線程的connection  
  53.         Connection conn = connectionHolder.get();  
  54.         //當前線程的connection不爲空時,關閉connection.  
  55.         if(conn != null){  
  56.             try{  
  57.                 conn.close();  
  58.                 //connection關閉之後,要從ThreadLocal的集合中清除Connection  
  59.                 connectionHolder.remove();  
  60.             }catch(SQLException e){  
  61.                 e.printStackTrace();  
  62.             }  
  63.   
  64.         }  
  65.     }  
  66. }  


      下面的代碼給大家演示了:ThreadLocal如何在同一個線程中可以共享Connection資源。


[java] view plain copy
 print?
  1. package com.bjpowernode.drp.flowcard.manager.impl;  
  2.   
  3. import java.sql.Connection;  
  4. import java.util.Date;  
  5. import com.bjpowernode.drp.flowcard.dao.FlowCardDao;  
  6. import com.bjpowernode.drp.flowcard.domain.FlowCard;  
  7. import com.bjpowernode.drp.flowcard.manager.FlowCardManager;  
  8. import com.bjpowernode.drp.util.ApplicationException;  
  9. import com.bjpowernode.drp.util.BeanFactory;  
  10. import com.bjpowernode.drp.util.ConnectionManager;  
  11. import com.bjpowernode.drp.util.DaoException;  
  12. import com.bjpowernode.drp.util.PageModel;  
  13.   
  14. public class FlowCardManagerImpl implements FlowCardManager {  
  15.   
  16.       
  17.     private FlowCardDao flowCardDao;  
  18.     //構造函數  
  19.     public FlowCardManagerImpl(){  
  20.         this.flowCardDao = (FlowCardDao) BeanFactory.getInstance().getDaoObject(FlowCardDao.class);  
  21.     }  
  22.       
  23.     @Override  
  24.     public void addFlowCard(FlowCard flowCard) throws ApplicationException {  
  25.           
  26.         Connection conn = null;  
  27.         try{  
  28.             //從ThreadLocal中獲取線程對應的Connection  
  29.             conn = ConnectionManager.getConnection();  
  30.             //開始事務  
  31.             ConnectionManager.beginTransaction(conn);  
  32.             //生成流向單單號  
  33.             String flowCardVouNo = flowCardDao.generateVouNo();  
  34.             //添加流向單主信息  
  35.             flowCardDao.addFlowCardMaster(flowCardVouNo, flowCard);  
  36.             //添加流向單明細信息  
  37.             flowCardDao.addFlowCardDetail(flowCardVouNo, flowCard.getFlowCardDetailList());  
  38.             //提交事務  
  39.             ConnectionManager.commitTransaction(conn);        
  40.         }catch(DaoException e){  
  41.             //回滾事務  
  42.             ConnectionManager.rollbackTransaction(conn);  
  43.             throw new ApplicationException("添加流向單失敗!");  
  44.         }finally{  
  45.             //關閉Connection並從ThreadLocal集合中清除  
  46.             ConnectionManager.closeConnection();  
  47.         }  
  48.       
  49.     }  
  50. }  

解析:

 

1、該類提供了線程局部變量,它獨立於變量的初始化副本

 

       大家可能對局部變量不太理解,爲什麼不是成員變量或全局變量,此時就涉及到變量的作用域問題。ThreadLocal具有比局部變量更大一點的作用域,在此作用域內資源可以共享,線程是安全的。

       我們還了解到ThreadLocal並不是本地線程,而是一個線程變量,它只是用來維護本地變量。針對每個線程提供自己的變量版本,避免了多線程的衝突問題,每個線程只需要維護自己的版本就好,彼此獨立,不會影響到對方。

 

2、每個線程有自己的一個ThreadLocal,修改它並不影響其他線程

  

      我們根據下面這張圖可以看到,向ThreadLocal裏面存東西就是創建了一個Map,一個線程對應一個Map集合,然後ThreadLocal把這個Map掛到當前的線程底下,一個key值對應一個value,這樣Map就只屬於當前線程。(如果您不理解Map的特點可以猛戳




3、在線程消失之後,其線程局部實例的所有副本都會被垃圾回收(除非存在對這些副本的其他引用)。 

 

      上面我們知道了變量副本的存放在了map中,當我們不在調用set,此時不在將引用指向該‘map’,而本線程退出時會執行資源回收操作,將申請的資源進行回收,其實就是將引用設置爲null。這時已經不在有任何引用指向該map,故而會被垃圾回收。


3、對比ThreadLocal和synchronized同步機制


相同點:

        1、ThreadLocal和線程同步機制都能解決多線程中相同變量的訪問衝突問題。

不同點:

       1、適用的情況不同

 

        在同步機制中,使用同步保證同一時間只有一個線程訪問,不能同時訪問共享資源,否則就是出現錯誤。ThreadLocal則隔離了相關的資源,並在同一個線程中可以共享這個資源。彼此獨立,修改不會影響到對方。

 

       2、最終實現的效果不同

    

       對於多線程資源共享問題,同步機制採用了“以時間換空間”的方式,而ThreadLocal採用了“以空間換時間”的方式。前者僅提供一份變量,讓不同的線程排隊訪問,而後者爲每一個線程都提供了一份變量,因此可以同時訪問而互不影響。

 

      上面博客的鏈接同樣也是線程同步機制synchronized的實例,大家可以通過兩個實例體會一下它們的異同點,再加上異同點解析,相信您對它們已經有了很深刻的認識。


4、一句話總結ThreadLocal


       ThreadLocal是解決線程安全問題一個很好的思路,在很多情況下,ThreadLocal比直接使用synchronized同步機制解決線程安全問題更簡單,更方便,並且程序擁有更高的併發性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章