近日筆者被多線程與單例對象之間的關係產生了混淆。經過了一段時間的查閱,理清了二者之間的管理,現作筆記梳理。若有不足,歡迎指出:)前端
在我在考慮考慮他們的時候思考了如下幾個問題:spring
一、咱們一般都將dao層(數據庫鏈接層)設置成單例,這樣的話若是每次處理數據庫中的數據都須要同一個對象去處理的話,處理數據的性能徹底得不到保證。數據庫
二、ssh中爲何struts2中的action層必須建立多例?而ssm中springmvc的Controller層不須要建立多例?安全
三、一個單例模式建立的對象是能夠同時被多個線程處理的,若是一個對象被多個線程同時處理的話,頗有可能出現線程同步問題(關於線程中的安全問題,對對象加鎖請參考:http://lavasoft.blog.51cto.com/62575/99155/),若是兩個線程同時訪問一個函數的話,要不要加鎖呢,加鎖怎麼加,不加又怎樣?session
理解思路:多線程
咱們知道一個對象是能夠同時被多個線程進行調用的,一個單例對象每次都只能有一個線程進行調用的話,就不會出現線程中的安全問題了。只有一個當一個對象加鎖後,才能作到每次只能有一個線程進行處理。mvc
多個線程處理一個對象的過程是怎麼樣的呢?每次建立一個線程,jvm虛擬機就會在內存中給每一個線程分配獨立的堆棧存儲空間。ssh
單例模式建立的對象在內存中是如何加載的呢?全部的單例模式的對象都是經過靜態方法獲取的,也就是說,每一個單例模式的對象都是存儲在靜態共享區中!jvm
一個普通對象在一個線程中是怎麼加載的呢?函數
在一個線程中,若是建立一個對象,會先在堆內存中開闢一個存儲空間,該對象在建立的時候就會在棧內存中開闢空間,調用其構造方法、靜態代碼塊等…當方法結束,會在棧內存中清除。一個線程在調用這個對象的方法的時候會在棧內存中建立一個空間存儲對象方法。
同理,對於在一個線程加載單例模式對象的時候,它會在靜態共享區獲取單例對象,而後在調用對象方法(非靜態)的時候會在本身的棧內存開闢一個空間,因此每一個線程在調用同一個對象的方法的時候都會在它的棧內存中開闢一個獨立的內存空間。這說明了多線程處理同一個對象的時候若是不涉及到對象的共有屬性值,不會存在線程安全問題。
如今對文章開頭的內容進行一點解釋:
一、咱們一般都將dao層(數據庫鏈接層)設置成單例,這樣的話若是每次處理數據庫中的數據都須要同一個對象去處理的話,處理數據的性能徹底得不到保證。
答:由於咱們每次處理數據庫是使用session進行數據庫的交互處理,而session是由SessionFactory建立的,SessionFactory將建立出的session放入session鏈接池中,鏈接池中的session均爲不一樣的對象。咱們指的建立單例對象在這裏指的是SessionFactory。
二、ssh中爲何struts2中的action層必須建立多例?而ssm中springmvc的Controller層不須要建立多例?
答:struts2中由於將前端獲取的值所有都存儲在對象屬性中,因此確定須要設置爲多例,springMVC中,前端獲取的值直接進入方法中,因此設置爲單例模式不會存在線程安全問題。
三、一個單例模式建立的對象是能夠同時被多個線程處理的,若是一個對象被多個線程同時處理的話,頗有可能出現線程同步問題(關於線程中的安全問題,對對象加鎖請參考:http://lavasoft.blog.51cto.com/62575/99155/),若是兩個線程同時訪問一個函數的話,要不要加鎖呢,加鎖怎麼加,不加又怎樣?
答:一個單例模式的方法能夠同時被多個線程處理,多個線程若是不是同時處理一個對象的共有屬性,則不會出現線程問題,即便是方法中的屬性若是兩個線程同時訪問同一個方法的時候,若是這個方法中沒有共有的屬性,則不須要加鎖,反之則須要加鎖。