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

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

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

    狂奔 lion

    自強不息

    2010年7月9日

    淺談Java中的同步的方法和原理

    Java的內存模型中Thread會附有自己的堆棧,寄存器,必要時需要和主存即heap之間同步。
    可以使用Synchornized關鍵字和Concurrent包中的Lock可以保證線程互斥和可見性。

    互斥性體現在類鎖或者對象鎖上,每個對象自身都包含一個監視器,該監視器是一個每次只能被一個線程所獲取進入的臨界區,可以通過wait和notify來退出和準入臨界區。可以看出這是一個生產者-消費者的模型。而Concurrent包中的Lock為了能夠獲得更好的性能和更好的擴展性,以及不依賴于關鍵字的可讀代碼,自己實現了這樣一個生產消費隊列,也就是AbstractQueuedSynchronizer,被稱為AQS的機制。每個Lock都內置了一個AbstractQueuedSynchronizer。需要說明的是AbstractQueuedSynchronizer內部實現采用了CAS機制,通過getState, setState, compareAndSetState訪問控制一個32bit int的形式進行互斥。

    那么可見性是如何保證的呢?

    對于關鍵字的同步機制,其實可見性就是線程和主存之間的同步時機問題。共有4個時間點需要注意:
    1 獲取或釋放類鎖/對象鎖的時候。Thread保證reload/flush全部變更
    2 volatile就是flush on write或者reload on read
    3 當線程首次訪問共享變量時,可以得到最新的結果。
    題外:所以在構造方法中公布this時很危險的。簡單的說,就是構造時不逃脫任何變量,不開啟新的線程,只做封裝。關于安全構造,請參考
    http://www.ibm.com/developerworks/cn/java/j-jtp0618/#resources
    4 線程結束時,所有變更會寫回主存

    關于Concurrent Lock如何實現可見性的問題,Doug Lea大俠,只在他的論文中提到,按照JSR133,Unsafe在getState, setState, compareAndSetState時保證了線程的變量的可見性,不需要額外的volatile支持,至于具體這些native做了哪些magic就不得而知了,總之,最后的contract就是保證lock區間的共享變量可見性。開發團隊被逼急了就這樣回答:
    There seems to be a real reluctance to explain the dirty details. I think the question was definitely understood on the concurrent interest thread, and the answer is that synchronized and concurrent locking are intended to be interchangable in terms of memory semantics when implemented correctly. The answer to matfud's question seems to be "trust us.”

    不過這個地方的確是開發團隊給我們用戶迷惑的地方,在同樣應用了CAS機制的Atomic類中,都內嵌了volatile變量,但是再lock塊中,他告訴我們可以保證可見性。

    感興趣的同學可以下面的兩個thread和Doug Lea的thesis:
    http://altair.cs.oswego.edu/pipermail/concurrency-interest/2005-June/001587.html
    http://forums.sun.com/thread.jspa?threadID=631014&start=15&tstart=0
    http://gee.cs.oswego.edu/dl/papers/aqs.pdf

    posted @ 2010-07-09 19:49 楊一 閱讀(1858) | 評論 (0)編輯 收藏

    2010年7月7日

    commons-net FTPClient API存取設計

    文件系統無非就是文件的存取和組織結構。
    訪問一個文件系統的API也應該是寫,讀,定位方法(Pathname?/URI?)

    FTPClient針對文件的保存和獲取各提供了兩個方法,分別是:

    public boolean storeFile(String remote, InputStream local)
    public OutputStream storeFileStream(String remote)

    public boolean retrieveFile(String remote, OutputStream local)
    public InputStream retrieveFileStream(String remote)

     

    兩個方法貌似相同,實際不同,返回流的那個因為不能馬上處理流,所以需要用戶手工調用completePendingCommand,而另一個傳遞流進去的則不需要。可能有同學已經遇到過這個問題了,讀寫第一個文件時總是正確的,當相同API讀寫第二個文件時,block住了。這是因為FTPClient要求在進行流操作之后執行completePendingCommand,以確保流處理完畢,因為流處理不是即時的,所以也沒有辦法不手工調用completePendingCommand。問題是開發者把不返回流的方法末尾加上了completePendingCommand,如果不看代碼可能根本不知道。
    文檔上說:

         * There are a few FTPClient methods that do not complete the
         
    * entire sequence of FTP commands to complete a transaction.  These
         
    * commands require some action by the programmer after the reception
         
    * of a positive intermediate command.  After the programmer's code
         * completes its actions, it must call this method to receive
         
    * the completion reply from the server and verify the success of the
         
    * entire transaction.


    但是這樣仍然還是讓人有點困惑,為什么都是存儲/讀取的方法,有時候要調用completePendingCommand,有時候不調用?更嚴重的問題是completePendingCommand調用了getReply,如果一個命令通過socket stream傳了過去但是沒有getReply,即沒有completePendingCommand,那么下次發命令時,將會受到本次返回碼的干擾,得到無效的響應。而如果在completePendingCommand之后又進行了一次無辜的completePendingCommand,那么因為FTP Server上沒有Reply了,就會block。所以completePendingCommand并不是可以隨意添加的。

    現在出現了兩個問題:
    1 completePendingCommand很容易多出來或遺漏
    2 顯式調用completePendingCommand暴露了底層實現,給用戶帶來不便,用戶只想要InputStream或者OutputStream

    為了解決這個問題,可以對InputStream進行擴展,建立一個ReplyOnCloseInputStream,如下:

    private static ReplyOnCloseInputStream extends InputStream{
      
    //
      public ReplyOnCloseInputStream(InputStream is, FTPClient c){
        
    //
      }

      
    //
      @override
      
    public void close(){
        
    if(c.completePendingCommand){
          is.close();
        }
    else{
          
    //throw Exception
        }

      }

    }
     
    //
    return new ReplyOnCloseInputStream(is, client);


    這樣封裝之后,FTPClient的用戶只需要正常在處理完流之后關閉即可,而不必暴露實現細節。保存文件也可以用相同的方法封裝OutputStream。

    posted @ 2010-07-07 23:08 楊一 閱讀(3463) | 評論 (1)編輯 收藏

    2010年7月2日

    關于ThreadLocal的內存泄露

    ThreadLocal是一種confinement,confinement和local及immutable都是線程安全的(如果JVM可信的話)。因為對每個線程和value之間存在hash表,而線程數量未知,從表象來看ThreadLocal會存在內存泄露,讀了代碼,發現實際上也可能會內存泄露。

    事實上每個Thread實例都具備一個ThreadLocal的map,以ThreadLocal Instance為key,以綁定的Object為Value。而這個map不是普通的map,它是在ThreadLocal中定義的,它和普通map的最大區別就是它的Entry是針對ThreadLocal弱引用的,即當外部ThreadLocal引用為空時,map就可以把ThreadLocal交給GC回收,從而得到一個null的key。

    這個threadlocal內部的map在Thread實例內部維護了ThreadLocal Instance和bind value之間的關系,這個map有threshold,當超過threshold時,map會首先檢查內部的ThreadLocal(前文說過,map是弱引用可以釋放)是否為null,如果存在null,那么釋放引用給gc,這樣保留了位置給新的線程。如果不存在slate threadlocal,那么double threshold。除此之外,還有兩個機會釋放掉已經廢棄的threadlocal占用的內存,一是當hash算法得到的table index剛好是一個null key的threadlocal時,直接用新的threadlocal替換掉已經廢棄的。另外每次在map中新建一個entry時(即沒有和用過的或未清理的entry命中時),會調用cleanSomeSlots來遍歷清理空間。此外,當Thread本身銷毀時,這個map也一定被銷毀了(map在Thread之內),這樣內部所有綁定到該線程的ThreadLocal的Object Value因為沒有引用繼續保持,所以被銷毀。

    從上可以看出Java已經充分考慮了時間和空間的權衡,但是因為置為null的threadlocal對應的Object Value無法及時回收。map只有到達threshold時或添加entry時才做檢查,不似gc是定時檢查,不過我們可以手工輪詢檢查,顯式調用map的remove方法,及時的清理廢棄的threadlocal內存。需要說明的是,只要不往不用的threadlocal中放入大量數據,問題不大,畢竟還有回收的機制。

    綜上,廢棄threadlocal占用的內存會在3中情況下清理:
    1 thread結束,那么與之相關的threadlocal value會被清理
    2 GC后,thread.threadlocals(map) threshold超過最大值時,會清理
    3 GC后,thread.threadlocals(map) 添加新的Entry時,hash算法沒有命中既有Entry時,會清理

    那么何時會“內存泄露”?當Thread長時間不結束,存在大量廢棄的ThreadLocal,而又不再添加新的ThreadLocal(或新添加的ThreadLocal恰好和一個廢棄ThreadLocal在map中命中)時。

    posted @ 2010-07-02 18:27 楊一 閱讀(2280) | 評論 (2)編輯 收藏

    2010年6月29日

    關于軟件文檔,我的看法

    文檔應該包括兩大部分,一部分是清晰的代碼結構和注釋,比如Concurrent API就是這樣,還有一部分是文字文檔,包括三個小部分:一是開發文檔,應該講架構和功能;二是索引文檔,詳細介紹功能和參數,三是用戶文檔,包括安裝和使用說明

    文檔最困難的莫過于版本的一致性,當軟件升級后,一些obsolete的內容和新的feature很難同步。要是架構發生了變化,那就更困難了。一般document team都不是太精于技術,所以也會產生一些問題。

    只能說任何事物永遠都有改進的空間,但是同樣也永遠沒有達到完美的程度

    posted @ 2010-06-29 18:26 楊一 閱讀(320) | 評論 (0)編輯 收藏

    2010年6月25日

    NIO學習之Web服務器示例

         摘要: 1 根據cpu core數量確定selector數量 2 用一個selector服務accept,其他selector按照core-1分配線程數運行 3 accept selector作為生產者把獲得的請求放入隊列 4 某個selector作為消費者從blocking queue中取出請求socket channel,并向自己注冊 5 當獲得read信號時,selector建立工作...  閱讀全文

    posted @ 2010-06-25 19:19 楊一 閱讀(1967) | 評論 (0)編輯 收藏

    多線程的知識

    多線程的優點:
    1 多核利用
    2 為單個任務建模方便
    3 異步處理不同事件,不必盲等
    4 現代的UI也需要它
    風險:
    1 同步變量易錯誤
    2 因資源限制導致線程活躍性問題
    3 因2導致的性能問題
    用途:
    框架,UI,Backend
    線程安全的本質是什么:
    并非是線程和鎖,這些只是基礎結構,本質是如何控制共享變量訪問的狀態
    什么是線程安全:
    就是線程之間的執行還沒有發生錯誤,就是沒有發生意外
    一個線程安全的類本身封裝了對類內部方法和變量的異步請求,調用方無需考慮線程安全問題
    無狀態的變量總是線程安全的
    原子性:
    完整執行的單元,如不加鎖控制,則會發生競態條件,如不加鎖的懶漢單例模式,或者復合操作。
    鎖,內在鎖,重入:
    利用synchronized關鍵字控制訪問單元,同一線程可以重入鎖內部,避免了面向對象產生的問題。同一變量的所有出現場合應該使用同一個鎖來控制。synchronized(lock)。
    即使所有方法都用synchronized控制也不能保證線程安全,它可能在調用時編程復合操作。
    活躍性和性能問題:
    過大的粒度會導致這個問題,用鎖進行異步控制,導致了線程的順序執行。
    簡單和性能是一對矛盾,需要適當的取舍。不能在沒有考慮成熟的情況下,為了性能去犧牲簡潔性。
    要盡量避免耗時操作,IO和網絡操作中使用鎖

    posted @ 2010-06-25 19:17 楊一 閱讀(374) | 評論 (0)編輯 收藏

    Ext Store Filter的實現和問題

    Store包含兩個數據緩存 - snapshot和data,grid,combo等控件的顯示全部基于data,而snapshot是數據的完整緩存,當首次應用過濾器時,snapshot從data中備份數據,當應用過濾器時,filter從snapshot獲取一份完整的數據,并在其中進行過濾,過濾后的結果形成了data并傳遞給展示,及data總是過濾后的數據,而snapshot總是完整的數據,不過看名字讓人誤以為它們的作用正好相反。
    相應地,當進行store的增刪改時,要同時維護兩個緩存。
    問題
    Store包含兩個增加Record的方法,即insert和add,其中的insert沒有更新snapshot所以當重新應用filter時,即data被重新定義時,在data中使用insert新增的記錄是無效的。
    解決方法
    用add不要用insert,如果用insert,記得把數據寫進snapshot: store.snapshot.addAll(records)

    posted @ 2010-06-25 19:16 楊一 閱讀(1283) | 評論 (0)編輯 收藏

    Ext中Combo組件的聯動封裝

         摘要: 在Extjs中構造N級聯動下拉的麻煩不少,需定制下拉數據并設定響應事件。通過對Combo集合的封裝,無需自己配置Combo,只需設定數據和關聯層級,即可自動構造出一組支持正向和逆向過濾的聯動下拉并獲取其中某一個的實例。 如: 數據: Ext.test = {};       Ext.test.lcbdata&nb...  閱讀全文

    posted @ 2010-06-25 19:14 楊一 閱讀(1306) | 評論 (0)編輯 收藏

    前端框架動態組件和代碼生成之間的選擇

    目前主流的SSH開發架構中,為減輕開發者工作,便于管理開發過程,往往用到一些公共代碼和組件,或者采用了基于模版的代碼生成機制,對于后臺的DAO,Service等因為架構決定,代碼生成必不可少,但是在前端頁面的實現上,卻可以有兩種不同的思路,一種是把配置信息直接封裝成更高級別的組建,一種是進行代碼生成。請大家討論一下這兩種方案的優劣,這里先拋磚引玉了。

    相同點:
    配置信息:XML OR 數據庫

    控件化:
    優點:
    1 易于添加公共功能
    2 修改配置數據直接生效
    3 代碼結構清晰,對開發者友好
    缺點:
    1 重組內存中對象結構,性能沒有代碼生成好(但渲染時間相同)
    2 僅能控制組件自身封裝的配置,不支持個性化修改,如果配置文件不支持的參數,則控件不支持
    3 必須保證每個控件一個配置

    代碼生成:
    優點:
    1 性能較好
    2 易于定制內容
    3 可以只配置一個模版,然后做出多個簡單的修改
    缺點:
    1 不能針對多個頁面同時添加公共功能
    2 業務修改需要重新生成代碼
    3 開發者需要修改自動生成的代碼,并需要了解一些底層的實現結構

    =====================20091029
    代碼生成并不能提高工作效率,尤其是針對復雜的富客戶端開發
    開發組件可提提供一種有效的選項,但是在運行效率和內存處理上需要細心處理

    posted @ 2010-06-25 19:11 楊一 閱讀(452) | 評論 (0)編輯 收藏

    2009年3月6日

    Javascript工作流引擎代碼及實例

     

    最近在學習jBPMJavascript,所以按照一些相關概念自己寫了下面的200行代碼的“工作流引擎”,工作流管理系統包含了流程定義,引擎,及應用系統三個主要部分,下面的代碼實現了流程的分支合并,目前只支持一種環節上的遷移。拷貝到html,雙擊就可以跑起來。

     

    var workflowDef = {
             start:{
                       fn:
    "begin"//對應處理方法可以在內部定義,也可以在外部定義
                       next:[
    "task1","task2"]
             },
             end:
    "end",
             tasks:[{
                       id:
    "task1",
                       fn:
    function(){
                                alert(
    "執行任務一");
                       },
                       before:
    function(){
                                alert(
    "執行任務一前");
                       },
                       after:
    function(){
                                alert(
    "執行任務一后");
                       },
                       next:[
    "task4","task5"]
             },{
                       id:
    "task2",
                       fn:
    function(){
                                alert(
    "執行任務二");
                       },
                       before:
    function(){
                                alert(
    "執行任務二前");
                       },
                       after:
    function(){
                                alert(
    "執行任務二后");
                       },
                       next:[
    "task3"]
             },{
                       id:
    "task3",
                       fn:
    function(){
                                alert(
    "執行任務三");
                       },
                       before:
    function(){
                                alert(
    "執行任務三前");
                       },
                       after:
    function(){
                                alert(
    "執行任務三后");
                       },
                       
    //定義合并的數量
                       merge: 
    3,
                       next:
    "EOWF"
             },{
                       id:
    "task4",
                       fn:
    function(){
                                alert(
    "執行任務四");
                       },
                       before:
    function(){
                                alert(
    "執行任務四前");
                       },
                       after:
    function(){
                                alert(
    "執行任務四后");
                       },
                       next:[
    "task3"]
             },{
                       id:
    "task5",
                       fn:
    function(){
                                alert(
    "執行任務五");
                       },
                       before:
    function(){
                                alert(
    "執行任務五前");
                       },
                       after:
    function(){
                                alert(
    "執行任務五后");
                       },
                       next:[
    "task3"]
             }]
    }

     

     

    //////////定義引擎////////////

    Yi 
    = {};
    Yi.Utils 
    = {};
    Yi.Utils.execute 
    = function(o){
             
    if(typeof o != 'function')
                       eval(o)();
             
    else
                       o();
    }
    //工作流類
    Yi.Workflow 
    = function(workflowDef){
             
    this.def = workflowDef;
             
    this.tasks = this.def.tasks;
    }
    //public按照環節id查找查找
    Yi.Workflow.prototype.findTask 
    = function(taskId){
             
    for(var i=0;i<this.tasks.length;i++){
                       
    if(this.tasks[i].id == taskId)
                                
    return this.tasks[i];
             }
    }
    //public啟動工作流
    Yi.Workflow.prototype.start 
    = function(){
             
    this.currentTasks = [];
             Yi.Utils.execute(
    this.def.start.fn);
             
    for(var i=0;i<this.def.start.next.length;i++){
                       
    this.currentTasks[i] = this.findTask(this.def.start.next[i]);
                       Yi.Utils.execute(
    this.currentTasks[i].before);
             }
    }
    //private
    Yi.Workflow.prototype.findCurrentTaskById 
    = function(taskId){
             
    for(var i=0;i<this.currentTasks.length;i++){
                       
    if(this.currentTasks[i].id == taskId)
                                
    return this.currentTasks[i];
             }
             
    return null;
    }
    //private
    Yi.Workflow.prototype.removeFromCurrentTasks 
    = function(task){
             
    var temp = [];
             
    for(var i=0;i<this.currentTasks.length;i++){
                       
    if(!(this.currentTasks[i] == task))
                                temp.push(
    this.currentTasks[i]); 
             }
             
    this.currentTasks = temp;
             temp 
    = null;
    }
    //public觸發當前環節
    Yi.Workflow.prototype.signal 
    = function(taskId){
             
    //只處理當前活動環節
             
    var task = this.findCurrentTaskById(taskId);
             
    if(task == null){
                       alert(
    "工作流未流轉到此環節!");
                       
    return;
             }
             
    //對于合并的處理
             
    if(task.merge != undefined){
                       
    if(task.merge != 0){
                                alert(
    "工作流流轉條件不充分!");
                                
    return;
                       }
    else{
                                Yi.Utils.execute(task.before);
                       }        
             }
             
    //觸發當前環節
             Yi.Utils.execute(task.fn);
             
    //觸發后動作
             Yi.Utils.execute(task.after);
             
    //下一步如果工作流結束
             
    if(task.next === "EOWF"){
                       Yi.Utils.execute(
    this.def.end);
                       
    delete this.currentTasks;
                       
    return;
             }
             
    //遍歷下一步環節
             
    this.removeFromCurrentTasks(task);
             
    for(var i=0;i<task.next.length;i++){
                       
    var tempTask = this.findTask(task.next[i]);
                       
    if(!tempTask.inCurrentTasks)
                                
    this.currentTasks.push(tempTask);
                       
    if(tempTask.merge != undefined){
                                tempTask.merge
    --;
                                tempTask.inCurrentTasks 
    = true;
                       }
                       
    else
                                Yi.Utils.execute(tempTask.before);
             }
    }
    //public獲取當前的活動環節
    Yi.Workflow.prototype.getCurrentTasks 
    = function(){
             
    return this.currentTasks;
    }
    //public獲取流程定義
    Yi.Workflow.prototype.getDef 
    = function(){
             
    return this.def;
    }

     

    ////////應用系統///////////////
    var wf = new Yi.Workflow(workflowDef);
    alert(
    "啟動工作流");
    wf.start();
    alert(
    "嘗試手工執行任務3,返回工作流沒有流轉到這里");
    wf.signal(
    "task3");
    alert(
    "分支開始");
    alert(
    "手工執行任務1");
    wf.signal(
    "task1");
    alert(
    "手工執行任務2");
    wf.signal(
    "task2");
    alert(
    "手工執行任務4");
    wf.signal(
    "task4");
    alert(
    "手工執行任務5");
    wf.signal(
    "task5");
    alert(
    "手工執行任務3");
    wf.signal(
    "task3");
    function begin(){
             alert(
    "流程開始,該函數在外部定義");
    }
    function end(){
             alert(
    "流程結束");
    }

    posted @ 2009-03-06 17:39 楊一 閱讀(1990) | 評論 (1)編輯 收藏

    僅列出標題  下一頁
    <2025年5月>
    27282930123
    45678910
    11121314151617
    18192021222324
    25262728293031
    1234567

    導航

    公告

    本人在blogjava上發表的文章及隨筆除特別聲明外均為原創或翻譯,作品受知識產權法保護并被授權遵從 知識分享協議:署名-非商業性使用-相同方式共享 歡迎轉載,請在轉載時注明作者姓名(楊一)及出處(www.tkk7.com/yangyi)
    /////////////////////////////////////////
    我的訪問者

    常用鏈接

    留言簿(5)

    隨筆分類(55)

    隨筆檔案(55)

    相冊

    Java

    其他技術

    生活

    最新隨筆

    搜索

    積分與排名

    最新評論

    閱讀排行榜

    評論排行榜

    自強不息


    用心 - 珍惜時間,勇于創造
    主站蜘蛛池模板: 亚洲AV无码成人精品区蜜桃| 亚洲精品无码成人AAA片| 亚洲熟妇av一区二区三区下载| 免费网站看av片| 亚洲AV中文无码字幕色三| 久久国产精品免费看| 亚洲日本一区二区三区| 24小时日本韩国高清免费| 亚洲一卡二卡三卡| 女人被免费视频网站| 亚洲国产午夜精品理论片在线播放| 在线观看免费成人| 全部在线播放免费毛片| 亚洲天堂中文字幕在线| 免费成人在线电影| 亚洲一区二区三区深夜天堂| 日韩精品无码区免费专区 | 亚洲AV无码乱码国产麻豆穿越| 三级黄色在线免费观看| 亚洲成色999久久网站| 午夜福利不卡片在线播放免费| 国产亚洲精品影视在线| 免费一看一级毛片人| 成人影片一区免费观看| 亚洲精品自在线拍| 国产三级免费电影| 国产一二三四区乱码免费| 亚洲欧洲日产v特级毛片| 国产精品久久久久影院免费| 一级特黄录像免费播放中文版| 香蕉视频在线观看亚洲| 免费视频专区一国产盗摄| 无遮挡a级毛片免费看| 久久精品国产96精品亚洲| 最近高清国语中文在线观看免费 | 亚洲成人福利在线| 国产18禁黄网站免费观看| 99热在线免费观看| 国产精品亚洲一区二区三区| 亚洲最大成人网色| 国产精品国产午夜免费福利看|