近日筆者被多線程與單例對象之間的關系產生了混淆。通過了一段時間的查閱,理清了兩者之間的管理,現做筆記梳理。如有不足,歡迎指出:)
在我在考慮考慮他們的時候思考了以下幾個問題:
1、我們通常都將dao層(數據庫連接層)設置成單例,這樣的話如果每次處理數據庫中的數據都需要同一個對象去處理的話,處理數據的性能完全得不到保證。
2、ssh中為什么struts2中的action層必須創建多例?而ssm中springmvc的Controller層不需要創建多例?
3、一個單例模式創建的對象是可以同時被多個線程處理的,如果一個對象被多個線程同時處理的話,很有可能出現線程同步問題(關於線程中的安全問題,對對象加鎖請參考:http://lavasoft.blog.51cto.com/62575/99155/),如果兩個線程同時訪問一個函數的話,要不要加鎖呢,加鎖怎么加,不加又怎樣?
理解思路:
我們知道一個對象是可以同時被多個線程進行調用的,一個單例對象每次都只能有一個線程進行調用的話,就不會出現線程中的安全問題了。只有一個當一個對象加鎖后,才能做到每次只能有一個線程進行處理。
多個線程處理一個對象的過程是怎么樣的呢?每次創建一個線程,jvm虛擬機就會在內存中給每個線程分配獨立的堆棧存儲空間。
單例模式創建的對象在內存中是如何加載的呢?所有的單例模式的對象都是通過靜態方法獲取的,也就是說,每個單例模式的對象都是存儲在靜態共享區中!
一個普通對象在一個線程中是怎么加載的呢?

在一個線程中,如果創建一個對象,會先在堆內存中開辟一個存儲空間,該對象在創建的時候就會在棧內存中開辟空間,調用其構造方法、靜態代碼塊等…當方法結束,會在棧內存中清除。一個線程在調用這個對象的方法的時候會在棧內存中創建一個空間存儲對象方法。
同理,對於在一個線程加載單例模式對象的時候,它會在靜態共享區獲取單例對象,然后在調用對象方法(非靜態)的時候會在自己的棧內存開辟一個空間,所以每個線程在調用同一個對象的方法的時候都會在它的棧內存中開辟一個獨立的內存空間。這說明了多線程處理同一個對象的時候如果不涉及到對象的共有屬性值,不會存在線程安全問題。
現在對文章開頭的內容進行一點解釋:
1、我們通常都將dao層(數據庫連接層)設置成單例,這樣的話如果每次處理數據庫中的數據都需要同一個對象去處理的話,處理數據的性能完全得不到保證。
答:因為我們每次處理數據庫是使用session進行數據庫的交互處理,而session是由SessionFactory創建的,SessionFactory將創建出的session放入session連接池中,連接池中的session均為不同的對象。我們指的創建單例對象在這里指的是SessionFactory。
2、ssh中為什么struts2中的action層必須創建多例?而ssm中springmvc的Controller層不需要創建多例?
答:struts2中因為將前端獲取的值全部都存儲在對象屬性中,所以肯定需要設置為多例,springMVC中,前端獲取的值直接進入方法中,所以設置為單例模式不會存在線程安全問題。
3、一個單例模式創建的對象是可以同時被多個線程處理的,如果一個對象被多個線程同時處理的話,很有可能出現線程同步問題(關於線程中的安全問題,對對象加鎖請參考:http://lavasoft.blog.51cto.com/62575/99155/),如果兩個線程同時訪問一個函數的話,要不要加鎖呢,加鎖怎么加,不加又怎樣?
答:一個單例模式的方法可以同時被多個線程處理,多個線程如果不是同時處理一個對象的共有屬性,則不會出現線程問題,即使是方法中的屬性如果兩個線程同時訪問同一個方法的時候,如果這個方法中沒有共有的屬性,則不需要加鎖,反之則需要加鎖。
