<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    一、Spring單例模式與線程安全

    http://www.cnblogs.com/doit8791/p/4093808.html
    Spring框架里的bean,或者說組件,獲取實(shí)例的時(shí)候都是默認(rèn)的單例模式,這是在多線程開發(fā)的時(shí)候要尤其注意的地方。

     http://www.cnblogs.com/hoojo/archive/2011/05/05/2038101.html

    單例模式的意思就是只有一個(gè)實(shí)例。單例模式確保某一個(gè)類只有一個(gè)實(shí)例,而且自行實(shí)例化并向整個(gè)系統(tǒng)提供這個(gè)實(shí)例。這個(gè)類稱為單例類。
    當(dāng)多用戶同時(shí)請(qǐng)求一個(gè)服務(wù)時(shí),容器會(huì)給每一個(gè)請(qǐng)求分配一個(gè)線程,這是多個(gè)線程會(huì)并發(fā)執(zhí)行該請(qǐng)求多對(duì)應(yīng)的業(yè)務(wù)邏輯(成員方法),此時(shí)就要注意了,如果該處理邏輯中有對(duì)該單列狀態(tài)的修改(體現(xiàn)為該單列的成員屬性),則必須考慮線程同步問題
    同步機(jī)制的比較  ThreadLocal和線程同步機(jī)制相比有什么優(yōu)勢呢?ThreadLocal和線程同步機(jī)制都是為了解決多線程中相同變量的訪問沖突問題。 
      在同步機(jī)制中,通過對(duì)象的鎖機(jī)制保證同一時(shí)間只有一個(gè)線程訪問變量。這時(shí)該變量是多個(gè)線程共享的,使用同步機(jī)制要求程序慎密地分析什么時(shí)候?qū)ψ兞窟M(jìn)行讀寫,什么時(shí)候需要鎖定某個(gè)對(duì)象,什么時(shí)候釋放對(duì)象鎖等繁雜的問題,程序設(shè)計(jì)和編寫難度相對(duì)較大。 
      而ThreadLocal則從另一個(gè)角度來解決多線程的并發(fā)訪問。ThreadLocal會(huì)為每一個(gè)線程提供一個(gè)獨(dú)立的變量副本,從而隔離了多個(gè)線程對(duì)數(shù)據(jù)的訪問沖突。因?yàn)槊恳粋€(gè)線程都擁有自己的變量副本,從而也就沒有必要對(duì)該變量進(jìn)行同步了。ThreadLocal提供了線程安全的共享對(duì)象,在編寫多線程代碼時(shí),可以把不安全的變量封裝進(jìn)ThreadLocal。 
      由于ThreadLocal中可以持有任何類型的對(duì)象,低版本JDK所提供的get()返回的是Object對(duì)象,需要強(qiáng)制類型轉(zhuǎn)換。但JDK 5.0通過泛型很好的解決了這個(gè)問題,在一定程度地簡化ThreadLocal的使用
     概括起來說,對(duì)于多線程資源共享的問題,同步機(jī)制采用了“以時(shí)間換空間”的方式,而ThreadLocal采用了“以空間換時(shí)間”的方式。前者僅提供一份變量,讓不同的線程排隊(duì)訪問,而后者為每一個(gè)線程都提供了一份變量,因此可以同時(shí)訪問而互不影響。 
      Spring使用ThreadLocal解決線程安全問題 
      我們知道在一般情況下,只有無狀態(tài)的Bean才可以在多線程環(huán)境下共享,在Spring中,絕大部分Bean都可以聲明為singleton作用域。就是因?yàn)镾pring對(duì)一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非線程安全狀態(tài)采用ThreadLocal進(jìn)行處理,讓它們也成為線程安全的狀態(tài),因?yàn)橛袪顟B(tài)的Bean就可以在多線程中共享了。 
      一般的Web應(yīng)用劃分為展現(xiàn)層、服務(wù)層和持久層三個(gè)層次,在不同的層中編寫對(duì)應(yīng)的邏輯,下層通過接口向上層開放功能調(diào)用。在一般情況下,從接收請(qǐng)求到返回響應(yīng)所經(jīng)過的所有程序調(diào)用都同屬于一個(gè)線程
    ThreadLocal是解決線程安全問題一個(gè)很好的思路,它通過為每個(gè)線程提供一個(gè)獨(dú)立的變量副本解決了變量并發(fā)訪問的沖突問題。在很多情況下,ThreadLocal比直接使用synchronized同步機(jī)制解決線程安全問題更簡單,更方便,且結(jié)果程序擁有更高的并發(fā)性。 
    如果你的代碼所在的進(jìn)程中有多個(gè)線程在同時(shí)運(yùn)行,而這些線程可能會(huì)同時(shí)運(yùn)行這段代碼。如果每次運(yùn)行結(jié)果和單線程運(yùn)行的結(jié)果是一樣的,而且其他的變量的值也和預(yù)期的是一樣的,就是線程安全的。 或者說:一個(gè)類或者程序所提供的接口對(duì)于線程來說是原子操作或者多個(gè)線程之間的切換不會(huì)導(dǎo)致該接口的執(zhí)行結(jié)果存在二義性,也就是說我們不用考慮同步的問題。  線程安全問題都是由全局變量及靜態(tài)變量引起的。  
    若每個(gè)線程中對(duì)全局變量、靜態(tài)變量只有讀操作,而無寫操作,一般來說,這個(gè)全局變量是線程安全的;若有多個(gè)線程同時(shí)執(zhí)行寫操作,一般都需要考慮線程同步,否則就可能影響線程安全。
    1) 常量始終是線程安全的,因?yàn)橹淮嬖谧x操作。 
    2)每次調(diào)用方法前都新建一個(gè)實(shí)例是線程安全的,因?yàn)椴粫?huì)訪問共享的資源。
    3)局部變量是線程安全的。因?yàn)槊繄?zhí)行一個(gè)方法,都會(huì)在獨(dú)立的空間創(chuàng)建局部變量,它不是共享的資源。局部變量包括方法的參數(shù)變量和方法內(nèi)變量。
    有狀態(tài)就是有數(shù)據(jù)存儲(chǔ)功能。有狀態(tài)對(duì)象(Stateful Bean),就是有實(shí)例變量的對(duì)象  ,可以保存數(shù)據(jù),是非線程安全的。在不同方法調(diào)用間不保留任何狀態(tài)。
    無狀態(tài)就是一次操作,不能保存數(shù)據(jù)。無狀態(tài)對(duì)象(Stateless Bean),就是沒有實(shí)例變量的對(duì)象  .不能保存數(shù)據(jù),是不變類,是線程安全的。
    有狀態(tài)對(duì)象:
    無狀態(tài)的Bean適合用不變模式,技術(shù)就是單例模式,這樣可以共享實(shí)例,提高性能有狀態(tài)的Bean,多線程環(huán)境下不安全,那么適合用Prototype原型模式。Prototype: 每次對(duì)bean的請(qǐng)求都會(huì)創(chuàng)建一個(gè)新的bean實(shí)例。
    Struts2默認(rèn)的實(shí)現(xiàn)是Prototype模式。也就是每個(gè)請(qǐng)求都新生成一個(gè)Action實(shí)例,所以不存在線程安全問題。需要注意的是,如果由Spring管理action的生命周期, scope要配成prototype作用域。

     

    二、線程安全案例:

    SimpleDateFormat(下面簡稱sdf)類內(nèi)部有一個(gè)Calendar對(duì)象引用,它用來儲(chǔ)存和這個(gè)sdf相關(guān)的日期信息,例如sdf.parse(dateStr), sdf.format(date) 諸如此類的方法參數(shù)傳入的日期相關(guān)String, Date等等, 都是交友Calendar引用來儲(chǔ)存的.這樣就會(huì)導(dǎo)致一個(gè)問題,如果你的sdf是個(gè)static的, 那么多個(gè)thread 之間就會(huì)共享這個(gè)sdf, 同時(shí)也是共享這個(gè)Calendar引用, 并且, 觀察 sdf.parse() 方法,你會(huì)發(fā)現(xiàn)有如下的調(diào)用:
    Date parse() {
      calendar.clear(); // 清理calendar
      ... // 執(zhí)行一些操作, 設(shè)置 calendar 的日期什么的
      calendar.getTime(); // 獲取calendar的時(shí)間
    }
    這里會(huì)導(dǎo)致的問題就是, 如果 線程A 調(diào)用了 sdf.parse(), 并且進(jìn)行了 calendar.clear()后還未執(zhí)行calendar.getTime()的時(shí)候,線程B又調(diào)用了sdf.parse(), 這時(shí)候線程B也執(zhí)行了sdf.clear()方法, 這樣就導(dǎo)致線程A的的calendar數(shù)據(jù)被清空了(實(shí)際上A,B的同時(shí)被清空了). 又或者當(dāng) A 執(zhí)行了calendar.clear() 后被掛起, 這時(shí)候B 開始調(diào)用sdf.parse()并順利i結(jié)束, 這樣 A 的 calendar內(nèi)存儲(chǔ)的的date 變成了后來B設(shè)置的calendar的date
    這個(gè)問題背后隱藏著一個(gè)更為重要的問題--無狀態(tài):無狀態(tài)方法的好處之一,就是它在各種環(huán)境下,都可以安全的調(diào)用。衡量一個(gè)方法是否是有狀態(tài)的,就看它是否改動(dòng)了其它的東西,比如全局變量,比如實(shí)例的字段。format方法在運(yùn)行過程中改動(dòng)了SimpleDateFormat的calendar字段,所以,它是有狀態(tài)的。
      這也同時(shí)提醒我們?cè)陂_發(fā)和設(shè)計(jì)系統(tǒng)的時(shí)候注意下一下三點(diǎn):
      1.自己寫公用類的時(shí)候,要對(duì)多線程調(diào)用情況下的后果在注釋里進(jìn)行明確說明
      2.對(duì)線程環(huán)境下,對(duì)每一個(gè)共享的可變變量都要注意其線程安全性
      3.我們的類和方法在做設(shè)計(jì)的時(shí)候,要盡量設(shè)計(jì)成無狀態(tài)的
     三.解決辦法
      1.需要的時(shí)候創(chuàng)建新實(shí)例:
      說明:在需要用到SimpleDateFormat 的地方新建一個(gè)實(shí)例,不管什么時(shí)候,將有線程安全問題的對(duì)象由共享變?yōu)榫植克接卸寄鼙苊舛嗑€程問題,不過也加重了創(chuàng)建對(duì)象的負(fù)擔(dān)。在一般情況下,這樣其實(shí)對(duì)性能影響比不是很明顯的。
      2.使用同步:同步SimpleDateFormat對(duì)象
    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);
            }
        } 
    }
      說明:當(dāng)線程較多時(shí),當(dāng)一個(gè)線程調(diào)用該方法時(shí),其他想要調(diào)用此方法的線程就要block,多線程并發(fā)量大的時(shí)候會(huì)對(duì)性能有一定的影響。
      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, 也是將共享變量變?yōu)楠?dú)享,線程獨(dú)享肯定能比方法獨(dú)享在并發(fā)環(huán)境中能減少不少創(chuàng)建對(duì)象的開銷。如果對(duì)性能要求比較高的情況下,一般推薦使用這種方法。
      4.拋棄JDK,使用其他類庫中的時(shí)間格式化類:
      1.使用Apache commons 里的FastDateFormat,宣稱是既快又線程安全的SimpleDateFormat, 可惜它只能對(duì)日期進(jìn)行format, 不能對(duì)日期串進(jìn)行解析。
      2.使用Joda-Time類庫來處理時(shí)間相關(guān)問題
      做一個(gè)簡單的壓力測試,方法一最慢,方法三最快,但是就算是最慢的方法一性能也不差,一般系統(tǒng)方法一和方法二就可以滿足,所以說在這個(gè)點(diǎn)很難成為你系統(tǒng)的瓶頸所在。從簡單的角度來說,建議使用方法一或者方法二,如果在必要的時(shí)候,追求那么一點(diǎn)性能提升的話,可以考慮用方法三,用ThreadLocal做緩存。
      Joda-Time類庫對(duì)時(shí)間處理方式比較完美,建議使用。

    posted on 2016-06-16 14:48 youngturk 閱讀(235) 評(píng)論(0)  編輯  收藏 所屬分類: 筆試題

    <2016年6月>
    2930311234
    567891011
    12131415161718
    19202122232425
    262728293012
    3456789

    導(dǎo)航

    統(tǒng)計(jì)

    公告

    this year :
    1 jQuery
    2 freemarker
    3 框架結(jié)構(gòu)
    4 口語英語

    常用鏈接

    留言簿(6)

    隨筆分類

    隨筆檔案

    文章分類

    文章檔案

    相冊(cè)

    EJB學(xué)習(xí)

    Flex學(xué)習(xí)

    learn English

    oracle

    spring MVC web service

    SQL

    Struts

    生活保健

    解析文件

    搜索

    最新評(píng)論

    閱讀排行榜

    評(píng)論排行榜

    主站蜘蛛池模板: 亚洲AV无码AV日韩AV网站| 免费大黄网站在线观| 最好看的中文字幕2019免费| 国产一区二区三区免费观在线| 日韩少妇内射免费播放| 免费播放美女一级毛片| 美女被免费视频网站| 一进一出60分钟免费视频| jizz免费观看| 中文字幕无码毛片免费看| 日本一区午夜艳熟免费| 久久99精品国产免费观看| 色欲A∨无码蜜臀AV免费播 | 激情内射亚洲一区二区三区| 亚洲AV无码乱码在线观看裸奔| 亚洲国产香蕉碰碰人人| 精品亚洲成a人片在线观看| 亚洲国产精品网站久久| 亚洲狠狠成人综合网| 亚洲av无码成人精品国产| 日韩大片在线永久免费观看网站| a在线视频免费观看在线视频三区| 中文字幕视频在线免费观看| 蜜桃AV无码免费看永久| 成熟女人特级毛片www免费| 免费va人成视频网站全| 亚洲中文字幕无码久久综合网| 久久精品7亚洲午夜a| 亚洲制服丝袜一区二区三区| 亚洲s码欧洲m码吹潮| 美女视频黄.免费网址 | 中国china体内裑精亚洲日本| 国产精品亚洲专区无码WEB| a级毛片免费网站| 免费播放一区二区三区| 最近免费中文字幕视频高清在线看| 免费v片在线观看品善网| 久久91亚洲人成电影网站| 亚洲视频一区二区三区四区| 杨幂最新免费特级毛片| 久久久精品免费视频|