最近看到spring管理的bean爲單例的,當它與web容器整合的時候始終搞不太清除,就網上搜索寫資料,java
Tomcat與多線程,web
servlet是多線程執行的,多線程是容器提供的能力。 servlet爲了能併發執行, 是由於servlet被這些thread使用,tomcat裏建立響應的socketServer線程類接收請求鏈接,而後在再建立或引用對應的servlet實例來處理請求鏈接。servlet是單例的,只建立一次。因此最好不要使用serlvet中的實例字段。spring
spring中的bean對象默認是單例的,若是不想單例須要以下配置:數據庫
<bean id="user" class="..." singleton="false"/>編程
singleton就是配置這個bean是不是單例的,若是不寫,就是默認值true設計模式
當在多線程環境下,由於在spring中咱們使用的bean都是無狀態的 如service、dao,能夠保證線程安全,若是你用如struts2 action 它多是由狀態的, 因此必須是prototype,當單例的bean須要有共享變量是,譬如使用一個併發隊列,必須本身來保證線程安全問題,緩存
1、Spring單例模式與線程安全tomcat
Spring框架裏的bean,或者說組件,獲取實例的時候都是
默認的單例模式,這是在多線程開發的時候要尤爲注意的地方。
單例模式的意思就是隻有一個實例。單例模式確保某一個類只有一個實例,並且自行實例化並向整個系統提供這個實例。這個類稱爲單例類。
當多用戶同時請求一個服務時,
容器會給每個請求分配一個線程,這是
多個線程會併發執行該請求多對應的業務邏輯(
成員方法),此時就要注意了,若是該處理邏輯中有對該單列狀態的修改(體現爲該單列的成員屬性),則必須考慮線程同步問題。(看這個bean是否須要狀態成員變量,秒殺中的併發隊列是須要狀態的一種狀況)
同步機制的比較
ThreadLocal和線程同步機制相比有什麼優點呢?ThreadLocal和線程同步機制都是爲了解決多線程中相同變量的訪問衝突問題。
在同步機制中,經過
對象的鎖機制保證同一時間只有一個線程訪問變量。這時該變量是多個線程共享的,使用同步機制要求程序慎密地分析何時對變量進行讀寫,何時須要鎖定某個對象,何時釋放對象鎖等繁雜的問題,程序設計和編寫難度相對較大。
而ThreadLocal則從另外一個角度來解決多線程的併發訪問。ThreadLocal會爲每個線程提供一個
獨立的變量副本,從而隔離了多個線程對數據的訪問衝突。由於每個線程都擁有本身的變量副本,從而也就沒有必要對該變量進行同步了。
ThreadLocal提供了線程安全的共享對象,在編寫多線程代碼時,能夠把不安全的變量封裝進ThreadLocal。
因爲ThreadLocal中能夠持有任何類型的對象,低版本JDK所提供的get()返回的是Object對象,須要強制類型轉換。但JDK 5.0經過泛型很好的解決了這個問題,在必定程度地簡化ThreadLocal的使用
歸納起來講,對於多線程資源共享的問題,同步機制採用了「以時間換空間」的方式,而ThreadLocal採用了「以空間換時間」的方式。前者僅提供一份變量,讓不一樣的線程排隊訪問,然後者爲每個線程都提供了一份變量,所以能夠同時訪問而互不影響。
Spring使用ThreadLocal解決線程安全問題
咱們知道在通常狀況下,
只有無狀態的Bean才能夠在多線程環境下共享,在Spring中,絕大部分Bean均可以聲明爲singleton做用域。就是由於Spring對一些Bean(如
RequestContextHolder、
TransactionSynchronizationManager、
LocaleContextHolder等)中
非線程安全狀態採用ThreadLocal進行處理,讓它們也成爲線程安全的狀態,由於有狀態的Bean就能夠在多線程中共享了。
通常的Web應用劃分爲展示層、服務層和持久層三個層次,在不一樣的層中編寫對應的邏輯,下層經過接口向上層開放功能調用。在通常狀況下,從接收請求到返回響應所通過的全部程序調用都同屬於一個線程
ThreadLocal是解決線程安全問題一個很好的思路,它經過爲每一個線程提供一個獨立的變量副本解決了變量併發訪問的衝突問題。在不少狀況下,ThreadLocal比直接使用synchronized同步機制解決線程安全問題更簡單,更方便,且結果程序擁有更高的併發性。
若是你的代碼所在的進程中有多個線程在同時運行,而這些線程可能會同時運行這段代碼。若是每次運行結果和單線程運行的結果是同樣的,並且其餘的變量的值也和預期的是同樣的,就是線程安全的。 或者說:一個類或者程序所提供的接口對於線程來講是原子操做或者多個線程之間的切換不會致使該接口的執行結果存在二義性,也就是說咱們不用考慮同步的問題。
線程安全問題都是由全局變量及靜態變量引發的。
若每一個線程中對全局變量、靜態變量只有讀操做,而無寫操做,通常來講,這個全局變量是線程安全的;如有多個線程同時執行寫操做,通常都須要考慮線程同步,不然就可能影響線程安全。
1) 常量始終是線程安全的,由於只存在讀操做。
2)每次調用方法前都新建一個實例是線程安全的,由於不會訪問共享的資源。
3)
局部變量是線程安全的。由於每執行一個方法,都會在獨立的空間建立局部變量,它不是共享的資源。局部變量包括方法的參數變量和方法內變量。
有狀態就是有數據存儲功能。
有狀態對象(Stateful Bean),就是有實例變量的對象 ,能夠保存數據,是非線程安全的。在不一樣方法調用間不保留任何狀態。
無狀態就是一次操做,不能保存數據。
無狀態對象(Stateless Bean),就是沒有實例變量的對象 .不能保存數據,是不變類,是線程安全的。
有狀態對象:
無狀態的Bean適合用不變模式,技術就是單例模式,這樣能夠共享實例,提升性能。有狀態的Bean,多線程環境下不安全,那麼適合用Prototype原型模式。Prototype: 每次對bean的請求都會建立一個新的bean實例。
Struts2默認的實現是Prototype模式。也就是每一個請求都新生成一個Action實例,因此不存在線程安全問題。須要注意的是,若是由Spring管理action的生命週期, scope要配成prototype做用域。
2、線程安全案例:安全
SimpleDateFormat(下面簡稱sdf)類內部有一個Calendar對象引用,它用來儲存和這個sdf相關的日期信息,例如sdf.parse(dateStr), sdf.format(date) 諸如此類的方法參數傳入的日期相關String, Date等等, 都是交友Calendar引用來儲存的.這樣就會致使一個問題,若是你的sdf是個static的, 那麼多個thread 之間就會共享這個sdf, 同時也是共享這個Calendar引用, 而且, 觀察 sdf.parse() 方法,你會發現有以下的調用:
Date parse() {
calendar.clear(); // 清理calendar
... // 執行一些操做, 設置 calendar 的日期什麼的
calendar.getTime(); // 獲取calendar的時間
}
這裏會致使的問題就是, 若是 線程A 調用了 sdf.parse(), 而且進行了 calendar.clear()後還未執行calendar.getTime()的時候,線程B又調用了sdf.parse(), 這時候線程B也執行了sdf.clear()方法, 這樣就致使線程A的的calendar數據被清空了(實際上A,B的同時被清空了). 又或者當 A 執行了calendar.clear() 後被掛起, 這時候B 開始調用sdf.parse()並順利i結束, 這樣 A 的 calendar內存儲的的date 變成了後來B設置的calendar的date
這個問題背後隱藏着一個更爲重要的問題--無狀態:無狀態方法的好處之一,就是它在各類環境下,均可以安全的調用。衡量一個方法是不是有狀態的,就看它是否改動了其它的東西,好比全局變量,好比實例的字段。format方法在運行過程當中改動了SimpleDateFormat的calendar字段,因此,它是有狀態的。
這也同時提醒咱們在開發和設計系統的時候注意下一下三點:
1.本身寫公用類的時候,要對多線程調用狀況下的後果在註釋裏進行明確說明
2.對線程環境下,對每個共享的可變變量都要注意其線程安全性
3.咱們的類和方法在作設計的時候,要儘可能設計成無狀態的
三.解決辦法
1.須要的時候建立新實例:
說明:在須要用到SimpleDateFormat 的地方新建一個實例,無論何時,將有線程安全問題的對象由共享變爲局部私有都能避免多線程問題,不過也加劇了建立對象的負擔。在通常狀況下,這樣其實對性能影響比不是很明顯的。
2.使用同步:同步SimpleDateFormat對象
public class DateSyncUtil {
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static String formatDate(Date date)throws ParseException{
synchronized(sdf){
return sdf.format(date);
}
}
public static Date parse(String strDate) throws ParseException{
synchronized(sdf){
return sdf.parse(strDate);
}
}
}
說明:當線程較多時,當一個線程調用該方法時,其餘想要調用此方法的線程就要block,多線程併發量大的時候會對性能有必定的影響。
3.使用ThreadLocal:
public class ConcurrentDateUtil {
private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
public static Date parse(String dateStr) throws ParseException {
return threadLocal.get().parse(dateStr);
}
public static String format(Date date) {
return threadLocal.get().format(date);
}
}
或
public class ThreadLocalDateUtil {
private static final String date_format = "yyyy-MM-dd HH:mm:ss";
private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>();
public static DateFormat getDateFormat()
{
DateFormat df = threadLocal.get();
if(df==null){
df = new SimpleDateFormat(date_format);
threadLocal.set(df);
}
return df;
}
public static String formatDate(Date date) throws ParseException {
return getDateFormat().format(date);
}
public static Date parse(String strDate) throws ParseException {
return getDateFormat().parse(strDate);
}
}
說明:使用ThreadLocal, 也是將共享變量變爲獨享,線程獨享確定能比方法獨享在併發環境中能減小很多建立對象的開銷。若是對性能要求比較高的狀況下,通常推薦使用這種方法。
4.拋棄JDK,使用其餘類庫中的時間格式化類:
1.使用Apache commons 裏的FastDateFormat,宣稱是既快又線程安全的SimpleDateFormat, 惋惜它只能對日期進行format, 不能對日期串進行解析。
2.使用Joda-Time類庫來處理時間相關問題
作一個簡單的壓力測試,方法一最慢,方法三最快,可是就算是最慢的方法一性能也不差,通常系統方法一和方法二就能夠知足,因此說在這個點很難成爲你係統的瓶頸所在。從簡單的角度來講,建議使用方法一或者方法二,若是在必要的時候,追求那麼一點性能提高的話,能夠考慮用方法三,用ThreadLocal作緩存。
Joda-Time類庫對時間處理方式比較完美,建議使用。
=======singleton bean和singleton模式區別=============================
spring
項目加載的時候bean(一個bean對應某個類)自動建立(初始化,建一個實例),然後是每次調用bean的時候是注入的(不是從新new。。全部整個系統都是這個實例,,並且是spring自動提供的)
一:對於Spring中實現Singleton模式,是以
IOC容器爲單位(就是說在這個容器裏面bean實現Singleton),換句話說,
一個JVM可能有多個IOC容器,而在這個容器裏實現了singleton bean,
而Java中實現Singleton模式而言,只有一個JVM,jvm中某個class只有一個實例
二:
singleton模式中,singleton的class在整個JVM中只有一個instance,
Spring的Bean,你能夠一個class配置多個Bean,這個class就有了多個instance。
這個singleton是指在spring容器中,這個Bean是單實例的,是線程共享的。因此要求這些類都是線程安全的。也就是說,不能出現修改Bean屬性的方法,固然除了設值得那些setter。只要知足線程安全,這些bean均可以用singleton。並且咱們在絕大多數使用上,也是這樣用的,包括dao,service。
Beanfactory是Spring初始以靜態方式載入的,Spring的單例IOC是基於容器級的,因此這你都不用擔憂與考慮.
Spring工做原理
1、 IoC(Inversion of control): 控制反轉
一、IoC:
概念:控制權由對象自己轉向容器;由容器根據配置文件去建立實例並建立各個實例之間的依賴關係
核心:bean工廠;在Spring中,bean工廠建立的各個實例稱做bean
2、AOP(Aspect-Oriented Programming): 面向方面編程
一、 代理的兩種方式:
靜態代理:
針對每一個具體類分別編寫代理類;
針對一個接口編寫一個代理類;
動態代理:
針對一個方面編寫一個InvocationHandler,而後借用JDK反射包中的Proxy類爲各類接口動態生成相應的代理類
二、 AOP的主要原理:動態代理
Spring 已經用過一段時間了,感受Spring是個很不錯的框架。內部最核心的就是IOC了,
動態注入,讓一個對象的建立不用new了,能夠自動的生產,這其實就是利用java裏的反射
反射其實就是在運行時動態的去建立、調用對象,Spring就是在運行時,跟xml Spring的配置
文件來動態的建立對象,和調用對象裏的方法的 。
Spring還有一個核心就是AOP這個就是面向切面編程,能夠爲某一類對象 進行監督和控制(也就是
在調用這類對象的具體方法的先後去調用你指定的 模塊)從而達到對一個模塊擴充的功能。這些都是經過
配置類達到的。
Spring目的:就是讓對象與對象(模塊與模塊)之間的關係沒有經過代碼來關聯,都是經過配置類說明
管理的(Spring根據這些配置 內部經過
反射去動態的組裝對象)
要記住:Spring是一個容器,凡是在容器裏的對象纔會有Spring所提供的這些服務和功能。
Spring裏用的最經典的一個設計模式就是:模板方法模式。(這裏我都不介紹了,是一個很經常使用的設計模式)
Spring裏的配置是不少的,很難都記住,可是Spring裏的精華也無非就是以上的兩點,把以上兩點跟理解了
也就基本上掌握了Spring.
各個部分的做用:
1.springmvc請全部的請求都提交給DispatcherServlet,它會委託應用系統的其餘模塊負責負責對請求進行真正的處理工做。
2.DispatcherServlet查詢一個或多個HandlerMapping,找處處理請求的Controller.
3.DispatcherServlet請請求提交到目標Controller
4.Controller進行業務邏輯處理後,會返回一個ModelAndView
5.Dispathcher查詢一個或多個ViewResolver視圖解析器,找到ModelAndView對象指定的視圖對象
6.視圖對象負責渲染返回給客戶端。
爲何要使用Spring:
AOP 讓開發人員能夠建立非行爲性的關注點,稱爲橫切關注點,並將它們插入到應用程序代碼中。使用 AOP後,公共服務(好比日誌、持久性、事務等)就能夠分解成方面並應用到域對象上,同時不會增長域對象的對象模型的複雜性。
IOC 容許建立一個能夠構造對象的應用環境,而後向這些對象傳遞它們的協做對象。正如單詞 倒置 所代表的,IOC 就像反過來的JNDI。沒有使用一堆抽象工廠、服務定位器、單元素(singleton)和直接構造(straightconstruction),每個對象都是用其協做對象構造的。所以是由容器管理協做對象(collaborator)。
Spring即便一個AOP框架,也是一IOC容器。 Spring 最好的地方是它有助於您替換對象。有了Spring,只要用JavaBean屬性和配置文件加入依賴性(協做對象)。而後能夠很容易地在須要時替換具備相似接口的協做對象。
Spring 框架是一個分層架構,由 7 個定義良好的模塊組成。Spring模塊構建在覈心容器之上,核心容器定義了建立、配置和管理bean 的方式,如圖 1 所示。
組成 Spring 框架的每一個模塊(或組件)均可以單獨存在,或者與其餘一個或多個模塊聯合實現。每一個模塊的功能以下:
核心容器:核心容器提供 Spring框架的基本功能。核心容器的主要組件是BeanFactory,它是工廠模式的實現。BeanFactory使用控制反轉(IOC)模式將應用程序的配置和依賴性規範與實際的應用程序代碼分開。
Spring 上下文:Spring 上下文是一個配置文件,向 Spring框架提供上下文信息。Spring上下文包括企業服務,例如 JNDI、EJB、電子郵件、國際化、校驗和調度功能。
Spring AOP:經過配置管理特性,Spring AOP 模塊直接將面向方面的編程功能集成到了Spring框架中。因此,能夠很容易地使 Spring 框架管理的任何對象支持 AOP。Spring AOP 模塊爲基於Spring的應用程序中的對象提供了事務管理服務。經過使用 Spring AOP,不用依賴EJB組件,就能夠將聲明性事務管理集成到應用程序中。
Spring DAO:JDBCDAO抽象層提供了有意義的異常層次結構,可用該結構來管理異常處理和不一樣數據庫供應商拋出的錯誤消息。異常層次結構簡化了錯誤處理,而且極大地下降了須要編寫的異常代碼數量(例如打開和關閉鏈接)。SpringDAO的面向 JDBC 的異常聽從通用的 DAO 異常層次結構。
Spring ORM:Spring 框架插入了若干個 ORM 框架,從而提供了 ORM的對象關係工具,其中包括JDO、Hibernate 和 iBatis SQL Map。全部這些都聽從 Spring 的通用事務和DAO異常層次結構。
Spring Web 模塊:Web 上下文模塊創建在應用程序上下文模塊之上,爲基於Web的應用程序提供了上下文。因此,Spring 框架支持與 Jakarta Struts的集成。Web模塊還簡化了處理多部分請求以及將請求參數綁定到域對象的工做。
Spring MVC 框架:MVC 框架是一個全功能的構建 Web 應用程序的 MVC實現。經過策略接口,MVC框架變成爲高度可配置的,MVC 容納了大量視圖技術,其中包括JSP、Velocity、Tiles、iText 和 POI。
Spring 框架的功能能夠用在任何 J2EE服務器中,大多數功能也適用於不受管理的環境。Spring的核心要點是:支持不綁定到特定 J2EE服務的可重用業務和數據訪問對象。毫無疑問,這樣的對象能夠在不一樣 J2EE 環境 (Web或EJB)、獨立應用程序、測試環境之間重用。服務器