標籤: 網站設計

  • Uber在英推出電動車隊,BYD、Nissan響應

    新式交通運輸公司Uber在英國倫敦發表了全球首支電動車車隊,並由中國比亞迪(BYD)、日商Nissan響應,分別提供E6和LEAF組成一支50輛電動車的車隊。這支車隊在8月31日正式上路,可有效減少當地空污;若反應良好,還會拓展到其他英國城市。

    倫敦市長Sadiq Khan曾承諾要將倫敦市轉型為全球最環保的城市,並積極採用低碳排放車款、設置電動車充電站。今年三月,由BYD和英國ADL聯手研發的五輛電動雙層巴士正式在倫敦上路,是全球首個電動雙層巴士車隊。

    另一方面,Uber也曾在南非、葡萄牙測試驗動車專案,並推出Uber Green實驗性服務。Uber車隊也曾採用Toyota Prius油電混和車,藉此降低行駛時的碳排放量。

    英國Uber總經理Jo Bertram表示,Uber希望能建立一個節能減碳的共乘交通模式,與Nissan、BYD的合作代表這個決心,也將是個起點。目前,英國的Uber大約有六成是油電混和車款,倫敦市對於電動車的接受度相對較高。

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※帶您來了解什麼是 USB CONNECTOR  ?

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

    ※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※教你寫出一流的銷售文案?

    聚甘新

  • 電動車夯,立凱-KY 8月營收創歷史新高

    電池正極材料廠立凱-KY公布8月合併營收為1.47億元,年成長62.6%,月增23.1%,創下單月歷史新高;累計其前8月營收8.77億元、年增24.03%;公司指出,8月營收攀高,主因是受惠產品價格持續走揚、以及車電事業部新增加技術服務收入。

    立凱-KY並表示,公司在中國大陸與五龍電動車集團的合作亦有重要斬獲。9月4日在中國杭州開幕的二十國集團(G20)首腦峰會,中國作為接待地主國為體現國家綠色和可持續發展的理念,首次使用純電動汽車作為G20峰會首長貴賓接待用車,即採用210輛五龍旗下長江汽車自主研發的電動中巴車、電動商務車作為會議VIP接待用車和會議核心區域工作用車;而長江電動汽車不僅造型新穎,其科技配置、智慧駕馭模式更是為各國來賓所讚譽。

    立凱-KY已於上月底前完成與五龍的資本合作案,雙方預計將在資金及技術方面合作,以加速拓展大陸電動車市場版圖。

    (本文內容由授權提供)

    本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    台北網頁設計公司這麼多該如何選擇?

    ※智慧手機時代的來臨,RWD網頁設計為架站首選

    ※評比南投搬家公司費用收費行情懶人包大公開

    ※回頭車貨運收費標準

    聚甘新

  • 軟件架構的核心思想

    軟件架構的核心思想

    目錄

    • 前言
    • 一、軟件架構的定義
    • 二、軟件架構與設計
    • 三、軟件架構的幾個方面
      • 系統
      • 結構
      • 環境
      • 利益相關者
    • 四、軟件架構的結構特徵
      • 運行時結構
      • 模塊結構
    • 五、軟件架構的質量屬性
      • 可修改性
      • 可測試性
      • 可擴展性
      • 性能
      • 可用性
      • 安全性
      • 可部署性
    • 六、其他常見概念
      • 內聚
      • 耦合
      • 企業架構
      • 系統架構

    前言

    welcome to chenqionghe’s blog,架構能力其實更像是一種內功,需要我們不斷地去學習,讓我們用一張正能量的圖片開啟美好的學習生活,let’s do it~

    一、軟件架構的定義

    架構是一個系統的基本組織,涵蓋所包含的組件,組件之間的關係、組件與環境的關係,以及指導架構設計和演進的原則等內容

    二、軟件架構與設計

    軟件架構和軟件設計中都有“設計”的意思,但與後者相比,前者具有更高的抽象性和更廣的範圍

    • 軟件架構關注如何對系統中的結構和交互進行較高級別的描述,它關注的是那些與系統骨架相關的決策問題,例如:功能、組織 、技術、業務和質量標準等。
    • 軟件設計關注構成系統的部件或組件,以及子系統是如何組織的,關注的問題通常更接近代碼或模塊本身,例如:
      • 將代碼分成哪些模塊?如何組織?
      • 給不同的功能分配哪些類(或模塊)?
      • 對於類“C”應該使用哪種設計模式?
      • 在運行時對象之間是如何交互的?傳遞什麼消息?如何實施交互?

    三、軟件架構的幾個方面

    系統

    系統是以特定方式組織的組件集合,以實現特定的功能。
    軟件系統是其軟件組件的集合。一個系統通常可以劃分為若干個子系統。

    結構

    結構是根據某個指導規則或原則來組成或組織在一起的一組元素的集合,可以是軟件或硬件系統。
    軟件架構可以根據觀察者的上下文展示各個層次的結構

    環境

    軟件系統所在的上下文或環境對其軟件架構有直接的影響。
    這樣的上下因素可以是技術、商業、專業、操作等

    利益相關者

    任何對某個系統及其成功與否感興趣或關心的個體或團體,都是利益相關者。
    例如:架構師、開發團隊、客戶、項目經理和營銷團隊等

    四、軟件架構的結構特徵

    運行時結構

    在運行時創建的對象及其之間的交互方式經常決定部署架構。
    部署架構與可擴展性、性能、安全性和交互操作性等質量屬性密切相關

    模塊結構

    為了分解任務,如何拆分代碼並把代碼組織到模塊和包中,這與系統的可維護性和可修改性密切相關,因為

    • 在代碼組織過程中考慮到可擴展性的話,通常會將父類放在單獨定義好的具有恰當文檔和配置的包中,這樣就可以輕易地通過增加外部模塊進行擴展,而不需要處理太多的依賴關係
    • 對於那些依賴於外部或第三方開發者(庫、框架等)的代碼,通過會根據提供的安裝或部署步驟,從外部源手動或自動地獲取並補丁全部各種依賴。此類代碼還提供多種文檔(例如README、INSTALL)等,它們清楚地記錄了這些步驟

    五、軟件架構的質量屬性

    質量屬性是系統的可度量和可測試的特性,可用於評估系統在其指定環境中的非功能性需求方面的達成情況

    可修改性

    定義為對系統進行修改的容易程度,以及系統對理髮進行調整的靈活性。
    這是討論的修改不光是代碼的修改、部署的修改,而是任何層次上的修改。
    一般架構師對可修改性的興趣點如下:

    • 難點:對系統進行修改的難易程度
    • 成本:進行修改需要的時間和資源
    • 風險:任何與系統修改相關的風險

    代碼可讀性越強,其可修改性就越強,代碼的可修改性與可讀性成正比

    可測試性

    指一個軟件系統支持通過測試來檢測故障的程度。
    可測試性也可以認為是一個軟件系統向最終用戶和集成測試隱藏了多少bug
    一個系統的可測試性越好,它能隱藏的bug就越少。

    可擴展性

    指系統能夠適應不斷增長的負載需求,但同時要保證可接受的處理性能,一般分為兩大類:

    • 橫向(水平)擴展性。意味着通過向其中添加更多多的計算節點。
    • 縱向(垂直)擴展性。涉及系統單個節點中資源的添加或移除

    性能

    指系統在給定的計算資源內完成的工作量,完成的工作量和計算資源的比例(work/unit)越高,性能越高。

    計算資源的單位有以下幾種

    • 響應時間。一個函數或執行單元運行所需要的時間。
    • 延遲。某個系統被激活並提供響應所需的時間。
    • 吞吐量。系統處理信息的某種比率。

    可用性

    指系統處於完全可操作狀態的程度,以便在任何時候獲得調用請求時可以執行的能力

    • 可靠性
      系統的可用性和可靠性密切相關,系統越可靠,可用性就越高。
    • 故障恢復能力
      影響可用性的另一個因素是從故障中恢復的能力,包括了故障檢測、故障恢復、故障預防
    • 數據一致性
      CAP定理指出,系統的可用性與其數據一致性有密切聯繫。一致性和可用性一般膛會同時成立,因為可能通信失敗,系統可以在一致性或可用性之間進行選擇

    安全性

    避免被未經過身份驗證的訪問損害數據和邏輯,同時繼續向通過誰的其他系統和角色提供服務的一種能力。

    可部署性

    指軟件從開發環境到產品運行環境移交的難易程度。
    有以下相關因素:

    • 模塊結構。
      將系統劃分為易於部署的一個個子單元,則部署會容易
    • 產品運行環境與開發環境。與
      開發環境結構非常相似的產品運行環境會使部署
    • 開發生態系統支持。
      為系統提供成熟的工具鏈支持,允許各種依賴關係自動建立和驗證等配置項內容,從而提高可部署性。
    • 標準配置。
      一個好的方式是開發者保持開發環境的配置結構和產品運行環境一致。
    • 標準化基礎設施。
      將部署保持在一個標準化的基礎設施上,提高可部署性
    • 容器使用
      隨着Docker容器技術的普及,可以規範軟件,減少啟動/停止的開銷,從而使部署更容易。

    六、其他常見概念

    內聚

    一個模塊內相關聯程度的度量,描述的是模塊內的功能聯繫
    若一個模塊之間各元素聯繫緊密,則內聚性就高(高內聚)
    如果能做到將模塊做成一個功能類聚、獨立性強、內部緊密結合才是一個理想的類聚模塊,這對初學都來說,非常不容易,不光是個人技術能力的挑戰,更是對某個領域業務水平的挑戰。

    耦合

    各模塊間相互聯繫緊密程度的一種度量。
    模塊之間聯繫少,耦合性就越低,模塊之間的相對獨立性就越強。

    企業架構

    企業架構是一個定義企業結構和行為的概念藍圖。它確定了企業結構、流程、人員和信息流動如何與其核心目標相一致,以便有效地實現當前和未來的目標

    系統架構

    系統架構是系統的基本組織形式,由其結構和行為視圖表示。該結構由兩部分確定:構成系統的組件和組件的行為。組件的行為是指組件之間的交互,以及組件與外部系統之間的交互

    以上內容由chenqionghe整理,參考《軟件架構-Python語言實現》,light weight baby~

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※帶您來了解什麼是 USB CONNECTOR  ?

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

    ※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※教你寫出一流的銷售文案?

    聚甘新

  • [經驗棧]C#監測IPv4v6網速及流量

    [經驗棧]C#監測IPv4v6網速及流量

    1、前言

      最近做項目需要用到監測網速及流量,我經過百度和牆內谷歌都沒能快速發現監測IPV6流量和網速的用例;也經過自己的一番查詢和調試,浪費了不少時間,現在作為經驗分享出來希望大家指正。

    2、C#代碼

    using System.Net.NetworkInformation;
    using System.Timers;
    
    namespace Monitor
    {
        public class MonitorNetwork
        {      
            public string UpSpeed { get; set; }   
            public string DownSpeed { get; set; }
            public string AllTraffic { get; set; }            
            private string NetCardDescription { get; set; }    
            //建立連接時上傳的數據量
            private long BaseTraffic { get; set; }    
            private long OldUp { get; set; }    
            private long OldDown { get; set; }
            private NetworkInterface networkInterface { get; set; }
            private Timer timer = new Timer() { Interval = 1000 };
        
            public void Close()
            {
                timer.Stop();   
            }
        
            public MonitorNetwork(string netCardDescription)
            {   
                timer.Elapsed += Timer_Elapsed;    
                NetCardDescription = netCardDescription;    
                timer.Interval = 1000;     
            }
    
            public bool Start()
            {
                networkInterface = null;    
                NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();    
                foreach (var var in nics)
                {
                    if (var.Description.Contains(NetCardDescription))
                    {
                        networkInterface = var;
                        break;
                    }
                }    
                if (networkInterface == null)
                {
                    return false;
                }
                else
                {    
                    BaseTraffic = (networkInterface.GetIPStatistics().BytesSent +
                                   networkInterface.GetIPStatistics().BytesReceived);    
                    OldUp = networkInterface.GetIPStatistics().BytesSent;    
                    OldDown = networkInterface.GetIPStatistics().BytesReceived;   
                    timer.Start();    
                    return true;
                }
        
            }
    
            private string[] units = new string[] {"KB/s","MB/s","GB/s" };
    
            private void CalcUpSpeed()
            {
                long nowValue = networkInterface.GetIPStatistics().BytesSent;    
                int num = 0;
                double value = (nowValue - OldUp) / 1024.0;
                while (value > 1023)
                {
                    value = (value / 1024.0);
                    num++;
                }   
                UpSpeed = value.ToString("0.0") + units[num];    
                OldUp = nowValue;    
            }
        
            private void CalcDownSpeed()
            {
                long nowValue = networkInterface.GetIPStatistics().BytesReceived;   
                int num = 0;
                double value = (nowValue - OldDown) / 1024.0;     
                while (value > 1023)
                {
                    value = (value / 1024.0);
                    num++;
                }    
                DownSpeed = value.ToString("0.0") + units[num];    
                OldDown = nowValue;    
            }
        
            private string[] unitAlls = new string[] { "KB", "MB", "GB" ,"TB"};
        
            private void CalcAllTraffic()
            {
                long nowValue = OldDown+OldUp;    
                int num = 0;
                double value = (nowValue- BaseTraffic) / 1024.0;
                while (value > 1023)
                {
                    value = (value / 1024.0);
                    num++;
                }   
                AllTraffic = value.ToString("0.0") + unitAlls[num];
            }
    
            private void Timer_Elapsed(object sender, ElapsedEventArgs e)
            {
                CalcUpSpeed();
                CalcDownSpeed();
                CalcAllTraffic();
            }
        }
    }
    

    3、胡說八道

      雖然沒能直接快速地百度到方法,但是實現這個需求的時候,心裏是有個譜,Windows系統能監測到這個網速和流量,沒理由實現不了,只需要一個方法將這個信息讀取出來就好。最後實現這個需求是利用了System.Net.NetworkInformation這個程序集,但是這個程序集沒有隻接提供網速監測的方法,而是提供了接收和發送數據量的屬性,需要自己計算出即使網速,所以這個網速不是特別的準確。

      這個程序集其實一開始就看到了,前輩方法中使用的是IPv4InterfaceStatistics類中的BytesReceived屬性和BytesSent屬性實現的,但是在這個程序集里沒有對應的IPv6類,恍恍惚惚。

      然後呢,我就下意識以為這個程序集比較老舊,不支持IPv6統計信息讀取,然後也是各種搜索無果,之後呢不死心想再來研究研究,東點點西瞅瞅,然後在NetworkInterface 類中發現了一個GetIPStatistics()方法,它的描述是“獲取此 NetworkInterface 實例的 IP 統計信息。”。

      然後就順理成章的事了,根據GetIPStatistics()返回的IPInterfaceStatistics實例中的BytesReceived屬性和BytesSent屬性就能獲取到收發的數據總量,然後根據這個信息就能計算出大約的網速。

      經測試,利用IPInterfaceStatistics實例是能讀取到IPv4和IPv6的總數據量的,因為這次的需求就是監測總量,如果需要單獨監測IPv6的可以用總量減去IPv4部分。

    4、後記

    ​  老師以前喊我認真念書,我心想有百度還不夠嗎,再念能有百度聰明,有百度懂得多,後來漸漸明白,百度懂得多都是前輩的搬磚添瓦來的,共勉。

    參考資料

      System.Net.NetworkInformation 命名空間

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    台北網頁設計公司這麼多該如何選擇?

    ※智慧手機時代的來臨,RWD網頁設計為架站首選

    ※評比南投搬家公司費用收費行情懶人包大公開

    ※回頭車貨運收費標準

    聚甘新

  • 【Spring】循環依賴 Java Vs Spring

    菜瓜:水稻,這次我特意去看了java的循環依賴

    水稻:喲,有什麼收穫

    菜瓜:兩種情況,構造器循環依賴,屬性循環依賴

    • 構造器循環依賴在邏輯層面無法通過。對象通過構造函數創建時如果需要創建另一個對象,就會存在遞歸調用。棧內存直接溢出
    • 屬性循環依賴可以解決。在對象創建完成之後通過屬性賦值操作。
    • package club.interview.base;
      
      /**
       * 構造器循環依賴 - Exception in thread "main" java.lang.StackOverflowError
       * toString()循環打印也會異常 - Exception in thread "main" java.lang.StackOverflowError
       * @author QuCheng on 2020/6/18.
       */
      public class Circular {
      
          class A {
              B b;
      
      //        public A() {
      //            b = new B();
      //        }
      
      //        @Override
      //        public String toString() {
      //            return "A{" +
      //                    "b=" + b +
      //                    '}';
      //        }
          }
      
          class B {
              A a;
      
      //        public B() {
      //            a = new A();
      //        }
      
      //        @Override
      //        public String toString() {
      //            return "B{" +
      //                    "a=" + a +
      //                    '}';
      //        }
          }
      
          private void test() {
              B b = new B();
              A a = new A();
              a.b = b;
              b.a = a;
              System.out.println(a);
              System.out.println(b);
          }
      
          public static void main(String[] args) {
              new Circular().test();
          }
      }

    水稻:厲害啊,Spring也不支持構造函數的依賴注入,而且也不支持多例的循環依賴。同樣的,它支持屬性的依賴注入。

    • 看效果 – 如果toString()打印同樣會出現棧內存溢出。
    • package com.vip.qc.circular;
      
      import org.springframework.stereotype.Component;
      
      import javax.annotation.Resource;
      
      /**
       * @author QuCheng on 2020/6/18.
       */
      @Component("a")
      public class CircularA {
      
          @Resource
          private CircularB circularB;
      
      //    @Override
      //    public String toString() {
      //        return "CircularA{" +
      //                "circularB=" + circularB +
      //                '}';
      //    }
      }
      
      
      package com.vip.qc.circular;
      
      import org.springframework.stereotype.Component;
      
      import javax.annotation.Resource;
      
      /**
       * @author QuCheng on 2020/6/18.
       */
      @Component("b")
      public class CircularB {
      
          @Resource
          private CircularA circularA;
      
      //    @Override
      //    public String toString() {
      //        return "CircularB{" +
      //                "circularA=" + circularA +
      //                '}';
      //    }
      }
      
      
          @Test
          public void testCircular() {
              String basePackages = "com.vip.qc.circular";
              new AnnotationConfigApplicationContext(basePackages);
          }

    菜瓜:看來spring的實現應該也是通過屬性注入的吧

    水稻:你說的對。先給思路和demo,之後帶你掃一遍源碼,follow me !

    • spring的思路是給已經初始化的bean標記狀態,假設A依賴B,B依賴A,先創建A
      • 先從緩存容器(總共三層,一級拿不到就拿二級,二級拿不到就從三級緩存中拿正在創建的)中獲取A,未獲取到就執行創建邏輯
      • 對象A在創建完成還未將屬性渲染完之前標記為正在創建中,放入三級緩存容器。渲染屬性populateBean()會獲取依賴的對象B。
      • 此時B會走一次getBean邏輯,B同樣會先放入三級緩存,然後渲染屬性,再次走getBean邏輯注入A,此時能從三級緩存中拿到A,並將A放入二級容器。B渲染完成放入一級容器
      • 回到A渲染B的方法populateBean(),拿到B之後能順利執行完自己的創建過程。放入一級緩存
    •  為了證實結果,我把源碼給改了一下,看結果

      • package com.vip.qc.circular;
        
        import org.springframework.stereotype.Component;
        
        import javax.annotation.Resource;
        
        /**
         * @author QuCheng on 2020/6/18.
         */
        @Component("a")
        public class CircularA {
        
            @Resource
            private CircularB circularB;
        
            @Override
            public String toString() {
                return "CircularA{" +
                        "circularB=" + circularB +
                        '}';
            }
        }
        
        
        package com.vip.qc.circular;
        
        import org.springframework.stereotype.Component;
        
        import javax.annotation.Resource;
        
        /**
         * @author QuCheng on 2020/6/18.
         */
        @Component("b")
        public class CircularB {
        
            @Resource
            private CircularA circularA;
        
            @Override
            public String toString() {
                return "CircularB{" +
                        "circularA=" + circularA +
                        '}';
            }
        }
        
        
        測試代碼
        @Test
            public void testCircular() {
                String basePackages = "com.vip.qc.circular";
                new AnnotationConfigApplicationContext(basePackages);
        }
        
        測試結果(我改過源碼了)
        ---- 
        將a放入三級緩存
        將b放入三級緩存
        將a放入二級緩存
        將b放入一級緩存
        從二級緩存中拿到了a
        將a放入一級緩存

          

    • 再看源碼
      • 關鍵類處理getSingleton邏輯 – 緩存容器
        • public class DefaultSingletonBeanRegistry 
          
            /** Cache of singleton objects: bean name to bean instance. */
            // 一級緩存
              private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
          
              /** Cache of singleton factories: bean name to ObjectFactory. */
            // 三級緩存
              private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);
          
              /** Cache of early singleton objects: bean name to bean instance. */
            // 二級緩存
              private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);
      • 主流程 AbstractApplicationContext#refresh() -> DefaultListableBeanFactory#preInstantiateSingletons() -> AbstractBeanFactory#getBean() & #doGetBean()
        • protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
                @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
             /**
              * 處理FactoryBean接口名稱轉換 {@link BeanFactory#FACTORY_BEAN_PREFIX }
              */
             final String beanName = transformedBeanName(name);
                  ...
             // ①從緩存中拿對象(如果對象正在創建中且被依賴注入,會放入二級緩存)
             Object sharedInstance = getSingleton(beanName);
             if (sharedInstance != null && args == null) {
                ...
             }else {      
                      ...
                   if (mbd.isSingleton()) {
                     // ② 將創建的對象放入一級緩存
                      sharedInstance = getSingleton(beanName, () -> {
                         try {
                               // ③ 具體創建的過程,每個bean創建完成之後都會放入三級緩存,然後渲染屬性
                            return createBean(beanName, mbd, args);
                         }catch (BeansException ex) {
                           ...
             ...
             return (T) bean;
          } 
      • ①getSingleton(beanName) – 二級緩存操作
        • protected Object getSingleton(String beanName, boolean allowEarlyReference) {
             // 實例化已經完成了的放在singletonObjects
             Object singletonObject = this.singletonObjects.get(beanName);
             // 解決循環依賴
             if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
                synchronized (this.singletonObjects) {
                   singletonObject = this.earlySingletonObjects.get(beanName);
                   if (singletonObject == null && allowEarlyReference) {
                      ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                      if (singletonFactory != null) {
                         singletonObject = singletonFactory.getObject();
                         this.earlySingletonObjects.put(beanName, singletonObject);
                         if(beanName.equals("a")||beanName.equals("b")||beanName.equals("c"))
                            System.out.println("將"+beanName+"放入二級緩存");;
                         this.singletonFactories.remove(beanName);
                      }
                   }else if(singletonObject != null){
                      System.out.println("從二級緩存中拿到了"+beanName);
                   }
                }
             }
             return singletonObject;
          }
      • ② getSingleton(beanName,lamdba) – 一級緩存操作
        • public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
                Assert.notNull(beanName, "Bean name must not be null");
                synchronized (this.singletonObjects) {
                   Object singletonObject = this.singletonObjects.get(beanName);
                   if (singletonObject == null) {
                      if (this.singletonsCurrentlyInDestruction) {
                      ...
                      // 正在創建的bean加入singletonsCurrentlyInCreation - 保證只有一個對象創建,阻斷循環依賴
                      beforeSingletonCreation(beanName);
                                ...
                      try {
                         singletonObject = singletonFactory.getObject();
                      ...
                      finally {
                      ...
                         // 從singletonsCurrentlyInCreation中移除
                         afterSingletonCreation(beanName);
                      }
                      if (newSingleton) {
                         // 對象創建完畢 - 放入一級緩存(從其他緩存移除)
                         addSingleton(beanName, singletonObject);
                      }
                   }
                   return singletonObject;
                }
             }
                  
           //  -----  內部調用一級緩存操作
              protected void addSingleton(String beanName, Object singletonObject) {
                  synchronized (this.singletonObjects) {
                      if(beanName.equals("a")||beanName.equals("b")||beanName.equals("c"))
                          System.out.println("將"+beanName+"放入一級緩存");;
                      this.singletonObjects.put(beanName, singletonObject);
                      this.singletonFactories.remove(beanName);
                      this.earlySingletonObjects.remove(beanName);
                      this.registeredSingletons.add(beanName);
                  }
              }        
             
      • ③createBean(beanName, mbd, args) – 三級緩存操作
        • protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)throws BeanCreationException {
               ...
             if (instanceWrapper == null) {
                // 5* 實例化對象本身
                instanceWrapper = createBeanInstance(beanName, mbd, args);
             }
             ...
             boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                   isSingletonCurrentlyInCreation(beanName));
             if (earlySingletonExposure) {
                ...
                // 將創建好還未渲染屬性的bean 放入三級緩存
                addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
             }
          
             Object exposedObject = bean;
             try {
                // 渲染bean自身和屬性
                populateBean(beanName, mbd, instanceWrapper);
                // 實例化之後的後置處理 - init
                exposedObject = initializeBean(beanName, exposedObject, mbd);
             }
             catch (Throwable ex) {
             ...
             return exposedObject;
          }
            
            
             // ------------- 內部調用三級緩存操作 
             protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
                  Assert.notNull(singletonFactory, "Singleton factory must not be null");
                  synchronized (this.singletonObjects) {
                      if (!this.singletonObjects.containsKey(beanName)) {
                          if(beanName.equals("a")||beanName.equals("b")||beanName.equals("c"))
                              System.out.println("將"+beanName+"放入三級緩存");;
                          this.singletonFactories.put(beanName, singletonFactory);
                          this.earlySingletonObjects.remove(beanName);
                          this.registeredSingletons.add(beanName);
                      }
                  }
              }       

    菜瓜:demo比較簡單,流程大致明白,源碼我還需要斟酌一下,整體有了概念。這個流程好像是摻雜在bean的創建過程中,結合bean的生命周期整體理解可能會更深入一點

    水稻:是的。每個知識點都不是單一的,拿着bean的生命周期再理解一遍可能會更有收穫。

     

    討論

    • 為什麼是三級緩存,兩級不行嗎?
      • 猜測:理論上兩級也可以實現。多一個二級緩存可能是為了加快獲取的速度。假如A依賴B,A依賴C,B依賴A,C依賴A,那麼C在獲取A的時候只需要從二級緩存中就能拿到A了

    總結

    • Spring的處理方式和java處理的思想一致,構造器依賴本身是破壞語義和規範的
    • 屬性賦值–> 依賴注入 。 先創建對象,再賦值屬性,賦值的時候發現需要創建便生成依賴對象,被依賴對象需要前一個對象就從緩存容器中拿取即可

     

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※帶您來了解什麼是 USB CONNECTOR  ?

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

    ※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※教你寫出一流的銷售文案?

    聚甘新

  • Java併發編程(05):悲觀鎖和樂觀鎖機制

    Java併發編程(05):悲觀鎖和樂觀鎖機制

    本文源碼:GitHub·點這裏 || GitEE·點這裏

    一、資源和加鎖

    1、場景描述

    多線程併發訪問同一個資源問題,假如線程A獲取變量之後修改變量值,線程C在此時也獲取變量值並且修改,兩個線程同時併發處理一個變量,就會導致併發問題。

    這種并行處理數據庫的情況在實際的業務開發中很常見,兩個線程先後修改數據庫的值,導致數據有問題,該問題復現的概率不大,處理的時候需要對整個模塊體系有概念,才能容易定位問題。

    2、演示案例

    public class LockThread01 {
        public static void main(String[] args) {
            CountAdd countAdd = new CountAdd() ;
            AddThread01 addThread01 = new AddThread01(countAdd) ;
            addThread01.start();
            AddThread02 varThread02 = new AddThread02(countAdd) ;
            varThread02.start();
        }
    }
    class AddThread01 extends Thread {
        private CountAdd countAdd  ;
        public AddThread01 (CountAdd countAdd){
            this.countAdd = countAdd ;
        }
        @Override
        public void run() {
            countAdd.countAdd(30);
        }
    }
    class AddThread02 extends Thread {
        private CountAdd countAdd  ;
        public AddThread02 (CountAdd countAdd){
            this.countAdd = countAdd ;
        }
        @Override
        public void run() {
            countAdd.countAdd(10);
        }
    }
    class CountAdd {
        private Integer count = 0 ;
        public void countAdd (Integer num){
            try {
                if (num == 30){
                    count = count + 50 ;
                    Thread.sleep(3000);
                } else {
                    count = count + num ;
                }
                System.out.println("num="+num+";count="+count);
            } catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    

    這裏案例演示多線程併發修改count值,導致和預期不一致的結果,這是多線程併發下最常見的問題,尤其是在併發更新數據時。

    出現併發的情況時,就需要通過一定的方式或策略來控制在併發情況下數據讀寫的準確性,這被稱為併發控制,實現併發控制手段也很多,最常見的方式是資源加鎖,還有一種簡單的實現策略:修改數據前讀取數據,修改的時候加入限制條件,保證修改的內容在此期間沒有被修改。

    二、鎖的概念簡介

    1、鎖機制簡介

    併發編程中一個最關鍵的問題,多線程併發處理同一個資源,防止資源使用的衝突一個關鍵解決方法,就是在資源上加鎖:多線程序列化訪問。鎖是用來控制多個線程訪問共享資源的方式,鎖機制能夠讓共享資源在任意給定時刻只有一個線程任務訪問,實現線程任務的同步互斥,這是最理想但性能最差的方式,共享讀鎖的機制允許多任務併發訪問資源。

    2、悲觀鎖

    悲觀鎖,總是假設每次每次被讀取的數據會被修改,所以要給讀取的數據加鎖,具有強烈的資源獨佔和排他特性,在整個數據處理過程中,將數據處於鎖定狀態,例如synchronized關鍵字的實現就是悲觀機制。

    悲觀鎖的實現,往往依靠數據庫提供的鎖機制,只有數據庫層提供的鎖機制才能真正保證數據訪問的排他性,否則,即使在本系統中實現了加鎖機制,也無法保證外部系統不會修改數據,悲觀鎖主要分為共享讀鎖和排他寫鎖。

    排他鎖基本機制:又稱寫鎖,允許獲取排他鎖的事務更新數據,阻止其他事務取得相同的資源的共享讀鎖和排他鎖。若事務T對數據對象A加上寫鎖,事務T可以讀A也可以修改A,其他事務不能再對A加任何鎖,直到T釋放A上的寫鎖。

    3、樂觀鎖

    樂觀鎖相對悲觀鎖而言,採用更加寬鬆的加鎖機制。悲觀鎖大多數情況下依靠數據庫的鎖機制實現,以保證操作最大程度的獨佔性。但隨之而來的就是數據庫性能的大量開銷,特別是對長事務的開銷非常的占資源,樂觀鎖機制在一定程度上解決了這個問題。

    樂觀鎖大多是基於數據版本記錄機制實現,為數據增加一個版本標識,在基於數據庫表的版本解決方案中,一般是通過為數據庫表增加一個version字段來實現。讀取出數據時,將此版本號一同讀出,之後更新時,對此版本號加一。此時,將提交數據的版本數據與數據庫表對應記錄的當前版本信息進行比對,如果提交的數據版本號等於數據庫表當前版本號,則予以更新,否則認為是過期數據。樂觀鎖機制在高併發場景下,可能會導致大量更新失敗的操作。

    樂觀鎖的實現是策略層面的實現:CAS(Compare-And-Swap)。當多個線程嘗試使用CAS同時更新同一個變量時,只有其中一個線程能成功更新變量的值,而其它線程都失敗,失敗的線程並不會被掛起,而是被告知這次競爭中失敗,並可以再次嘗試。

    4、機制對比

    悲觀鎖本身的實現機制就以損失性能為代價,多線程爭搶,加鎖、釋放鎖會導致比較多的上下文切換和調度延時,加鎖的機制會產生額外的開銷,還有增加產生死鎖的概率,引發性能問題。

    樂觀鎖雖然會基於對比檢測的手段判斷更新的數據是否有變化,但是不確定數據是否變化完成,例如線程1讀取的數據是A1,但是線程2操作A1的值變化為A2,然後再次變化為A1,這樣線程1的任務是沒有感知的。

    悲觀鎖每一次數據修改都要上鎖,效率低,寫數據失敗的概率比較低,比較適合用在寫多讀少場景。

    樂觀鎖並未真正加鎖,效率高,寫數據失敗的概率比較高,容易發生業務形異常,比較適合用在讀多寫少場景。

    是選擇犧牲性能,還是追求效率,要根據業務場景判斷,這種選擇需要依賴經驗判斷,不過隨着技術迭代,數據庫的效率提升,集群模式的出現,性能和效率還是可以兩全的。

    三、Lock基礎案例

    1、Lock方法說明

    lock:執行一次獲取鎖,獲取后立即返回;

    lockInterruptibly:在獲取鎖的過程中可以中斷;

    tryLock:嘗試非阻塞獲取鎖,可以設置超時時間,如果獲取成功返回true,有利於線程的狀態監控;

    unlock:釋放鎖,清理線程狀態;

    newCondition:獲取等待通知組件,和當前鎖綁定;

    2、應用案例

    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    public class LockThread02 {
        public static void main(String[] args) {
            LockNum lockNum = new LockNum() ;
            LockThread lockThread1 = new LockThread(lockNum,"TH1");
            LockThread lockThread2 = new LockThread(lockNum,"TH2");
            LockThread lockThread3 = new LockThread(lockNum,"TH3");
            lockThread1.start();
            lockThread2.start();
            lockThread3.start();
        }
    }
    class LockNum {
        private Lock lock = new ReentrantLock() ;
        public void getNum (){
            lock.lock();
            try {
                for (int i = 0 ; i < 3 ; i++){
                    System.out.println("ThreadName:"+Thread.currentThread().getName()+";i="+i);
                }
            } finally {
                lock.unlock();
            }
        }
    }
    class LockThread extends Thread {
        private LockNum lockNum ;
        public LockThread (LockNum lockNum,String name){
            this.lockNum = lockNum ;
            super.setName(name);
        }
        @Override
        public void run() {
            lockNum.getNum();
        }
    }
    

    這裏多線程基於Lock鎖機制,分別依次執行任務,這是Lock的基礎用法,各種API的詳解,下次再說。

    3、與synchronized對比

    基於synchronized實現的鎖機制,安全性很高,但是一旦線程失敗,直接拋出異常,沒有清理線程狀態的機會。顯式的使用Lock語法,可以在finally語句中最終釋放鎖,維護相對正常的線程狀態,在獲取鎖的過程中,可以嘗試獲取,或者嘗試獲取鎖一段時間。

    四、源代碼地址

    GitHub·地址
    https://github.com/cicadasmile/java-base-parent
    GitEE·地址
    https://gitee.com/cicadasmile/java-base-parent
    

    推薦閱讀:Java基礎系列

    序號 文章標題
    A01 Java基礎:基本數據類型,核心點整理
    A02 Java基礎:特殊的String類,和相關擴展API
    B01 Java併發:線程的創建方式,狀態周期管理
    B02 Java併發:線程核心機制,基礎概念擴展
    B03 Java併發:多線程併發訪問,同步控制
    B04 Java併發:線程間通信,等待/通知機制

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    台北網頁設計公司這麼多該如何選擇?

    ※智慧手機時代的來臨,RWD網頁設計為架站首選

    ※評比南投搬家公司費用收費行情懶人包大公開

    ※回頭車貨運收費標準

    聚甘新

  • 從一個計算器開始說起——C#中的工廠方法模式

    從一個計算器開始說起——C#中的工廠方法模式

    工廠模式作為很常見的設計模式,在日常工作中出鏡率非常高,程序員們一定要掌握它的用法喲,今天跟着老胡一起來看看吧。

    舉個例子

    現在先讓我們來看一個例子吧,比如,要開發一個簡單的計算器,完成加減功能,通過命令行讀入形如1+1的公式,輸出2這個結果,讓我們看看怎麼實現吧。

     

    第一個版本

    這個版本裏面,我們不考慮使用模式,就按照最簡單的結構,怎麼方便怎麼來。

    思路非常簡單,僅需要實現以下幾個方法

    • 取運算數
    • 取運算符
    • 輸出結果
         class Program
        {
            static int GetOperatorIndex(string input)
            {
                int operatorIndex = 0;
                for (; operatorIndex < input.Length; operatorIndex++)
                {
                    if (!char.IsDigit(input[operatorIndex]))
                        break;
                }
                return operatorIndex;
            }
    
            static int GetOp(string input, int startIndex, int size = -1)
            {
                string subStr;
                if (size == -1)
                {
                    subStr = input.Substring(startIndex);
                }
                else
                {
                    subStr = input.Substring(startIndex, size);
                }
                return int.Parse(subStr);
            }
    
            static int CalculateExpression(string input)
            {
                var operatorIndex = GetOperatorIndex(input); //得到運算符索引
                var op1 = GetOp(input, 0, operatorIndex); //得到運算數1
                var op2 = GetOp(input, operatorIndex + 1); //得到運算數2
                switch (input[operatorIndex])
                {
                    case '+':
                        return op1 + op2;
                    case '-':
                        return op1 - op2;
                    default:
                        throw new Exception("not support");
                }
            }
    
            static void Main(string[] args)
            {
                string input = Console.ReadLine();
                while(!string.IsNullOrEmpty(input))
                {
                    var result = CalculateExpression(input);
                    Console.WriteLine("={0}", result);
                    input = Console.ReadLine();
                }            
            }
    }
    

    代碼非常簡單,毋庸置疑,這個運算器是可以正常工作的。這也可能是我們大部分人剛剛踏上工作崗位的時候可能會寫出的代碼。但它有着以下這些缺點:

    • 缺乏起碼的抽象,至少加和減應該能抽象出操作類。
    • 缺乏抽象造成了巨型客戶端,所有的邏輯都嵌套在了客戶端裏面。
    • 使用switch case缺乏擴展性,同時switch case也暗指了這部分代碼是屬於變化可能性比較高的地方,我們應該把它們封裝起來。而且不能把他們放在和客戶端代碼一起

    接下來,我們引入我們的主題,工廠方法模式。
     

    工廠方法模式版本

    工廠方法模式使用一個虛擬的工廠來完成產品構建(在這裡是運算符的構建,因為運算符是我們這個程序中最具有變化的部分),通過把可變化的部分封裝在工廠類中以達到隔離變化的目的。我們看看UML圖:

    依葫蘆畫瓢,我們設計思路如下:

    • 設計一個IOperator接口,對應抽象的Product
    • 設計AddOperator和SubtractOperator,對應具體Product
    • 設計IOperatorFactory接口生產Operator
    • 設計OperatorFactory實現抽象IFactory

    關鍵代碼如下,其他讀取操作數之類的代碼就不在贅述。

    • IOperator接口
           interface IOperator
           {
               int Calculate(int op1, int p2);
           }
    
    • 具體Operator
    	class AddOperator : IOperator
            {
                public int Calculate(int op1, int op2)
                {
                    return op1 + op2;
                }
            }
    
            class SubtractOperator : IOperator
            {
                public int Calculate(int op1, int op2)
                {
                    return op1 - op2;
                }
            }
    
    • Factory接口
    	interface IOperatorFactory
            {
                IOperator CreateOperator(char c);
            }
    
    • 具體Factory
    	class OperatorFactory : IOperatorFactory
            {
                public IOperator CreateOperator(char c)
                {
                    switch(c)
                    {
                        case '+':
                            return new AddOperator();
                        case '-':
                            return new SubtractOperator();
                        default:
                            throw new Exception("Not support");
                    }
                }
            }
    
    • 在CalculateExpression裏面使用他們
       static IOperator GetOperator(string input, int operatorIndex)
           {
               IOperatorFactory f = new OperatorFactory();
               return f.CreateOperator(input[operatorIndex]);
           }
    
           static int CalculateExpression(string input)
           {
               var operatorIndex = GetOperatorIndex(input);
               var op1 = GetOp(input, 0, operatorIndex);
               var op2 = GetOp(input, operatorIndex + 1);
               IOperator op = GetOperator(input, operatorIndex);
               return op.Calculate(op1, op2);                    
           }
    

    這樣,我們就用工廠方法重新寫了一次計算器,現在看看,好處有

    • 容易變化的創建部分被工廠封裝了起來,工廠和客戶端以接口的形式依賴,工廠內部邏輯可以隨時變化而不用擔心影響客戶端代碼
    • 工厂部分可以放在另外一個程序集,項目規劃會更加合理
    • 客戶端僅僅需要知道工廠和抽象的產品類,不需要再知道每一個具體的產品(不需要知道如何構建每一個具體運算符),符合迪米特法則
    • 擴展性增強,如果之後需要添加乘法multiple,那麼僅需要添加一個Operator類代表Multiple並且修改Facotry裏面的生成Operator邏輯就可以了,不會影響到客戶端
       

    自此,我們已經在代碼裏面實現了工廠方法模式,但可能有朋友就會想,雖然現在擴展性增強了,但是新添加運算符還是需要修改已有的工廠,這不是違反了開閉原則么。。有沒有更好的辦法呢?當然是有的。
     

    反射版本

    想想工廠方法那個版本,我們為什麼增加新的運算符就會不可避免的修改現有工廠?原因就是我們通過switch case來硬編碼“教導”工廠如何根據用戶輸入產生正確的運算符,那麼如果有一種方法可以讓工廠自動學會發現新的運算符,那麼我們的目的不就達到了?

    嗯,我想聰明的朋友們已經知道了,用屬性嘛,在C#中,這種方法完成類的自描述,是最好不過了的。

    我們的設計思路如下:

    • 定義一個描述屬性以識別運算符
    • 在運算符中添加該描述屬性
    • 在工廠啟動的時候,掃描程序集以註冊所有運算符

    代碼如下:

    • 描述屬性
        class OperatorDescriptionAttribute : Attribute
        {
            public char Symbol { get; }
            public OperatorDescriptionAttribute(char c)
            {
                Symbol = c;
            }
        }
    
    • 添加描述屬性到運算符
        [OperatorDescription('+')]
        class AddOperator : IOperator
        {
            public int Calculate(int op1, int op2)
            {
                return op1 + op2;
            }
        }
    
        [OperatorDescription('-')]
        class SubtractOperator : IOperator
        {
            public int Calculate(int op1, int op2)
            {
                return op1 - op2;
            }
        }
    
    • 讓工廠使用描述屬性
        class OperatorFactory : IOperatorFactory
        {
            private Dictionary<char, IOperator> dict = new Dictionary<char, IOperator>();
            public OperatorFactory()
            {
                Assembly assembly = Assembly.GetExecutingAssembly();
                foreach (var type in assembly.GetTypes())
                {
                    if (typeof(IOperator).IsAssignableFrom (type) 
                        && !type.IsInterface)
                    {
                        var attribute = type.GetCustomAttribute<OperatorDescriptionAttribute>();
                        if(attribute != null)
                        {
                            dict[attribute.Symbol] = Activator.CreateInstance(type) as IOperator;
                        }
                    }
                }
            }
            public IOperator CreateOperator(char c)
            {
                if(!dict.ContainsKey(c))
                {
                    throw new Exception("Not support");
                }
                return dict[c];
            }
        }
    

    經過這種改造,現在程序對擴展性支持已經很友好了,需要添加Multiple,只需要添加一個Multiple類就可以,其他代碼都不用修改,這樣就完美符合開閉原則了。

      [OperatorDescription('*')]
      class MultipleOperator : IOperator
      {
          public int Calculate(int op1, int op2)
          {
              return op1 * op2;
          }
      }
    

    這就是我們怎麼一步步從最原始的代碼走過來,一點點重構讓代碼實現工廠方法模式,最終再完美支持開閉原則的過程,希望能幫助到大家。

    其實關於最後那個通過標記屬性實現擴展,微軟有個MEF框架支持的很好,原理跟這個有點相似,有機會我們再聊聊MEF。

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※帶您來了解什麼是 USB CONNECTOR  ?

    ※自行創業缺乏曝光? 網頁設計幫您第一時間規劃公司的形象門面

    ※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!

    ※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

    ※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

    ※教你寫出一流的銷售文案?

    聚甘新

  • JAVA設計模式 1【創建型】設計模式介紹、單例模式的理解與使用

    JAVA設計模式 1【創建型】設計模式介紹、單例模式的理解與使用

    數據結構我們已經學了一部分了。是該了解了解設計模式了。習慣了CRUD的你,也該了解了解這一門神器、我為啥要說是神器呢?

    因為在大廠的面試環節、以及很多的比如

    • Springboot
    • Mybatis

    等開源框架中、大量的使用到了設計模式。為了我們在之後學習源代碼的時候不再懵逼,為啥這代碼能這樣寫?為啥巴拉巴拉xxx

    設計模式必須要肝完

    簡介

    設計模式,是一套被反覆使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結
    它是解決特定問題的一系列套路,是前輩們的代碼設計經驗的總結,具有一定的普遍性,可以反覆使用。其目的是為了提高代碼的可重用性、代碼的可讀性和代碼的可靠性。

    總結下來說就是:一種設計經驗、一種設計套路

    想一下,被前輩們總結下來的東西。使用這麼多年、凝結為精華的東西、該學

    創建型

    我們首先來了解一下什麼是創建型,創建型 作為設計模式的一種分類,是描述如何將一個對象創建出來的。

    我們都知道,JAVA 作為一種面向對象編程,最關鍵的關鍵字new 用來實例化一個對象。創建型分類、則是描述:如何更好的創建出一個對象

    單例模式理解

    單例模式,從字面意思上了解就是:它只負責創建出一個對象。因此被稱為單例模式。理解一下:我們的計算機都會有一個任務管理器。而在一台windows 的電腦上。任何時候都只會實例化一個任務管理器對象。進而可以理解為單例模式

    在一個任務管理器被調用的時候(對象已經被創建),再次使用ctrl+shift+esc 則任然返回這個已經被創建的任務管理器

    UML 圖理解

    可能我首次提到這個概念,所以簡介一下。

    UML圖是用圖形化的方式表示軟件類與類之間的關係。用圖形化的方式,展示出眾多類之間的關聯關係。

    類圖

    如下圖,我用圖形的方式、描述了一個任務管理器類JobManagement.class
    它有一個 私有化private的屬性management 類型為本身。
    它有一個 公開的public 的方法getManagement() 返回類型為本身

    • 常用的類型修飾符就是 + 與 –

    注意:“可見性”表示該屬性對類外的元素是否可見,包括公有(Public)、私有(Private)、受保護(Protected)和朋友(Friendly)4 種,在類圖中分別用符號+、-、#、~表示。
    http://c.biancheng.net/view/1319.html

    關聯關係

    關聯關係就是用來表示:多個類之間存在怎麼樣的關係的表示方法。常用箭頭來表示。

    虛線箭頭 依賴關係

    虛線箭頭用來表示依賴關係 從使用類指向被依賴的類。這裏使用的類就是我們的main 方法。而被依賴類則是我們的任務管理器對象

    菱形箭頭 聚合關係

    聚合管理作為一種強關聯管理。一般用於成員變量的引用。

    http://c.biancheng.net/view/1319.html

    單例模式的特點

    • 對象只會被創建一次,並且重複使用
    • 全局提供一個訪問點。靜態訪問點
    • 構造方法私有

    學以致用

    public class JobManagement {
    
        private static volatile JobManagement management;
    
        private JobManagement() {
    
        }
    
        public static synchronized JobManagement getManagement() {
    
            if (null == management) {
                System.out.println("未創建任務管理器,正在創建。。");
                management = new JobManagement();
            } else {
                System.out.println("已經存在創建的任務管理器");
            }
            return management;
        }
    }
    

    任務管理器對象包含以及靜態類型的自身對象引用。以及將自身的構造方法進行私有化、使得外部無法直接創建對象。而需要這個對象,則需要調用get()方法進行獲取。

    • volatile 關鍵字將屬性在所有線程中同步

    懶漢模式

    	if (null == management) {
                System.out.println("未創建任務管理器,正在創建。。");
                management = new JobManagement();
            } 
    

    懶漢模式則是在對象首次被訪問的時候才進行創建的。否則、若這個對象從未被引用、則對象是不會被創建的。而餓漢模式,剛剛相反。

    餓漢模式

    private static JobManagement management = new JobManagement();
    

    餓漢模式則是則是在類被虛擬機加載的時候就創建一個示例出來。這樣在訪問之前就已經有對象被創建、線程也是安全的。

    測試使用

        public static void main(String[] args) {
    
            JobManagement management1 = JobManagement.getManagement();
            System.out.println(management1);
            JobManagement management2 = JobManagement.getManagement();
            System.out.println(management2);
    
        }
    ----------------------------
    未創建任務管理器,正在創建。。
    JobManagement@1b6d3586
    已經存在創建的任務管理器
    JobManagement@1b6d3586
    

    小結

    單例模式在Java 的學習中還是有很多地方會使用到,對於我們學習的第一個,也是最簡單的模式,也是最常用的模式。記住它的特點:

    • 構造方法私有
    • 提供一個全局訪問點

    參考

    http://c.biancheng.net/view/1338.html

    歡迎關注

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    台北網頁設計公司這麼多該如何選擇?

    ※智慧手機時代的來臨,RWD網頁設計為架站首選

    ※評比南投搬家公司費用收費行情懶人包大公開

    ※回頭車貨運收費標準

    聚甘新

  • 荷蘭開放首條塑膠再製自行車道 約含50萬個瓶蓋

    摘錄自2018年09月18日科技新報報導

    荷蘭人以愛好騎單車聞名,該國約 1,700 萬人,卻擁有超過 2,200 萬輛腳踏車,光是阿姆斯特丹就鋪設了近 800 公里的自行車道,東北部城鎮茲沃勒(Zwolle)日前又正式開放一條全由回收塑膠再製生成的自行車道。

    該塑膠車道概念來自道路工程公司 KWS 的 Anne Koudstaal 和 Simon Jorritsma,道路全長 30 公尺,採用預製模塊,重量輕、易安裝,下方設計了排水系統讓管道、電纜通過,可讓水快速流通,遇到暴雨時還能充當臨時儲水槽、避免淹水。

    據估計,這條自行車道包含了約 218,000 個塑膠杯,或 50 萬個塑膠瓶蓋,使用年限可比傳統道路長 2~3 倍(有待觀察),路面應該也不會出現裂縫或坑洞。

     

    本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

  • 維珍航空成功生產環保飛機燃料 減低環境影響

    摘錄自2018年09月18日科技新報報導

    維珍航空與 LanzaTech 從 2011 年開始研發環保飛機燃料技術,最近宣布終於開發成功,從鋼鐵煉製廠的工業廢氣提煉了 1,500 加侖飛機燃料。

    此技術把原本排出大氣層的廢氣,透過發酵過程轉換成低碳乙醇 Lanzanol,首批 Lanzanol 在中國首鋼集團廠房生產。初步測試顯示,這種飛機燃料比傳統燃料減少 65% 碳排放,意味除了生產過程可持續,實際使用也相當環保。

    兩家公司希望未來擴充生產,預計如果把計劃推展到全球鋼鐵煉製廠,可生產目前全球所有航班燃料的五分之一。維珍航空也希望明年可使用這種新燃料首次測試飛行。

    本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

    台北網頁設計公司這麼多該如何選擇?

    ※智慧手機時代的來臨,RWD網頁設計為架站首選

    ※評比南投搬家公司費用收費行情懶人包大公開

    ※回頭車貨運收費標準

    聚甘新