一些基礎之一

高并發(fā)

面試的時候都喜歡問這個問題,解決高并發(fā)的問題根源在于解決高并發(fā)下共享資源的控制問題。也就牽扯到多線程下共享資源的同步問題。

  • 同步:單線程場景下,不需要考慮資源的同步,在執(zhí)行一個方法或者操作后,一直等待結果,當然這時候可以有個超時的處理機制,但是本質上還是一直阻塞的。
  • 異步:多線程場景下,就需要考慮異步操作,也就是做一件事,不影響別的操作。在執(zhí)行一個操作后,不去理會結果,而是去處理另外的操作,直到接收到已經(jīng)處理完成的通知或消息,才去處理對應的結果。

解決并發(fā)和同步問題,主要是通過鎖機制,不管是數(shù)據(jù)庫級別,還是緩存級別,都是通過鎖來完成。

  • 悲觀鎖,一般是數(shù)據(jù)庫級別,也就是鎖定對應的行級數(shù)據(jù),優(yōu)點是可以控制數(shù)據(jù)不出問題,缺點是性能方面
  • 樂觀鎖,一般通過采用version字段的方式來控制,不會鎖定對應的數(shù)據(jù),性能也比較高,但是會導致臟數(shù)據(jù),比如發(fā)券場景下的超發(fā)現(xiàn)象,以及商品庫存的超賣現(xiàn)象。
  • 對于單系統(tǒng)而言,在代碼級別使用syncrinized來控制代碼的訪問是可行的,但是在分布式環(huán)境下,只能通過數(shù)據(jù)庫鎖或者緩存鎖來控制。
  • 緩存鎖的優(yōu)勢:速度相對DB是比較快,可以有效降低DB的壓力。比如redis的watch,對應的jedis就需要使用setnx自己實現(xiàn)lock方法;比如tair在ldb的incr方法及put方法,incr方法指定lowBound和upBound(也就是一個取值范圍)可以解決這個問題,而put方法則傳入version版本來做,也是樂觀鎖。(參見:https://yq.aliyun.com/articles/58928
  • 當然數(shù)據(jù)庫可以通過分庫分表的方式來緩解壓力,對于老數(shù)據(jù)可以考慮增加歷史庫,以保證數(shù)據(jù)量不會影響對應的數(shù)據(jù)庫操作;而緩存也可以通過設置多個Key來分流單key的鎖競爭壓力。

設計模式 -- 后續(xù)每天補充

設計模式是代碼設計經(jīng)驗的總結,在代碼復用,穩(wěn)定性以及易理解方面都有一定的保證。具體參見:
http://www.cnblogs.com/maowang1991/archive/2013/04/15/3023236.html

  • 設計模式的六大原則
    1、開閉原則(Open Close Principle)
    開閉原則就是說對擴展開放,對修改關閉。在程序需要進行拓展的時候,不能去修改原有的代碼,實現(xiàn)一個熱插拔的效果。所以一句話概括就是:為了使程序的擴展性好,易于維護和升級。想要達到這樣的效果,我們需要使用接口和抽象類,后面的具體設計中我們會提到這點。
    2、里氏代換原則(Liskov Substitution Principle)
    里氏代換原則(Liskov Substitution Principle LSP)面向對象設計的基本原則之一。 里氏代換原則中說,任何基類可以出現(xiàn)的地方,子類一定可以出現(xiàn)。 LSP是繼承復用的基石,只有當衍生類可以替換掉基類,軟件單位的功能不受到影響時,基類才能真正被復用,而衍生類也能夠在基類的基礎上增加新的行為。里氏代換原則是對“開-閉”原則的補充。實現(xiàn)“開-閉”原則的關鍵步驟就是抽象化。而基類與子類的繼承關系就是抽象化的具體實現(xiàn),所以里氏代換原則是對實現(xiàn)抽象化的具體步驟的規(guī)范。
    3、依賴倒轉原則(Dependence Inversion Principle)
    這個是開閉原則的基礎,針對接口編程,依賴于抽象而不依賴于具體。
    4、接口隔離原則(Interface Segregation Principle)
    使用多個隔離的接口,比使用單個接口要好。降低類之間的耦合度的意思,從這兒我們看出,其實設計模式就是一個軟件的設計思想,從大型軟件架構出發(fā),為了升級和維護方便。所以上文中多次出現(xiàn):降低依賴,降低耦合。
    5、迪米特法則(最少知道原則)(Demeter Principle)
    為什么叫最少知道原則,就是說:一個實體應當盡量少的與其他實體之間發(fā)生相互作用,使得系統(tǒng)功能模塊相對獨立。
    6、合成復用原則(Composite Reuse Principle)
    原則是盡量使用合成/聚合的方式,而不是使用繼承。
工廠模式

實現(xiàn)同一個接口的對象,如果需要大量創(chuàng)建,則使用工廠模式。工廠模式又可以細分為普通工廠模式和靜態(tài)工廠模式。以及為了解決上述工廠模式需要修改原有代碼的抽象工廠模式

  • 接口及實現(xiàn)
    // interface
    public interface Sender { public void send();}
    //mailSender
    public class MailSender implements Sender { public void send() { System.out.println("Mail Sender"); }}
    // smsSender
    public class SmsSender implements Sender { public void send() { System.out.println("Sms Sender"); }}
  • 普通工廠模式:
    public class NormalSenderFactory {
    public Sender mailSender(){ return new MailSender(); }
    public Sender smsSender(){ return new SmsSender(); }
    }

  • 靜態(tài)工廠模式
    public class StaticSenderFactory {
    public static Sender mailSender(){ return new MailSender(); }
    public static Sender smsSender(){ return new SmsSender(); }
    }

  • 調用方式
    public class SenderFactoryTest {
    public static void main(String[] args){
    NormalSenderFactory senderFactory = new NormalSenderFactory();
    senderFactory.mailSender().send();
    senderFactory.smsSender().send();
    // 靜態(tài)工廠模式調用
    StaticSenderFactory.smsSender().send();
    StaticSenderFactory.mailSender().send(); }
    }

  • 抽象工廠模式
    工廠方法模式有一個問題就是,對象的創(chuàng)建依賴工廠類,也就是說,如果想要拓展程序,必須對工廠類進行修改,這違背了閉包原則。所以也就有了抽象工廠模式。
    抽象工廠模式增加一個新的接口,用于封裝原有的工廠,也就是說可以創(chuàng)建不同的工廠來處理不同的業(yè)務。

  • 接口及實現(xiàn)
    //provider
    public interface Provider { public Sender produce();}
    // smsFactory
    public class SmsFactoryProvider implements Provider {
    public Sender produce() { return new SmsSender(); }
    }
    // mailFactory
    public class MailFactoryProvider implements Provider {
    public Sender produce() { return new MailSender(); }
    }
  • 調用方式
    public class AbstractFactoryTest {
    public static void main(String[] args){
    Provider provider = new SmsFactoryProvider(); provider.produce().send();
    }
    }
單例模式

保證只有一個對象存在,注意在多線程場景下,需要使用synchronized來鎖住對應的instance,也就是需要用synchronized來鎖定對應的對象
注意的是,采用類的靜態(tài)方法,實現(xiàn)單例模式的效果,也是可行的,此處二者有什么不同?

  • 首先,靜態(tài)類不能實現(xiàn)接口。(從類的角度說是可以的,但是那樣就破壞了靜態(tài)了。因為接口中不允許有static修飾的方法,所以即使實現(xiàn)了也是非靜態(tài)的)
  • 其次,單例可以被延遲初始化,靜態(tài)類一般在第一次加載是初始化。之所以延遲加載,是因為有些類比較龐大,所以延遲加載有助于提升性能。
  • 再次,單例類可以被繼承,他的方法可以被覆寫。但是靜態(tài)類內部方法都是static,無法被覆寫。
  • 最后一點,單例類比較靈活,畢竟從實現(xiàn)上只是一個普通的Java類,只要滿足單例的基本需求,你可以在里面隨心所欲的實現(xiàn)一些其它功能,但是靜態(tài)類不行。從上面這些概括中,基本可以看出二者的區(qū)別,但是,從另一方面講,我們上面最后實現(xiàn)的那個單例模式,內部就是用一個靜態(tài)類來實現(xiàn)的,所以,二者有很大的關聯(lián),只是我們考慮問題的層面不同罷了。兩種思想的結合,才能造就出完美的解決方案,就像HashMap采用數(shù)組+鏈表來實現(xiàn)一樣,其實生活中很多事情都是這樣,單用不同的方法來處理問題,總是有優(yōu)點也有缺點,最完美的方法是,結合各個方法的優(yōu)點,才能最好的解決問題!
建造者模式

相當于將多個工廠的調用放到一起,創(chuàng)建批量的不同子類型的復合體復雜對象,如下:

public class SenderBuilder {    
    private List<Sender> senderList = Lists.newArrayList();    

    public List<Sender> buildMail(int count){        
         for(int i=0;i<count;i++){            senderList.add(new MailSender());        }        
         return senderList;    
    }    

   public List<Sender> buildSms(int count){        
       for(int i=0;i<count;i++){            senderList.add(new SmsSender());        }        
       return senderList;    }
   }

 // test類
 public class BuilderTest {    
       public static void main(String[] args){        
          SenderBuilder senderBuilder = new SenderBuilder();        
          senderBuilder.buildMail(10);    
   }}
原型模式
適配器模式

將一種接口轉換成另外一種接口,以消除接口不匹配造成的兼容性問題。

裝飾模式

需要(動態(tài))擴展一個類的功能,實現(xiàn)同一個接口,并且調用原有類,只不過在原有類對應方法的前后加入自己的業(yè)務處理邏輯。

代理模式

跟裝飾模式其實很相似,都是去擴展一些原有類的功能,不過代理模式會隱藏掉原有類的對象及入口,而裝飾模式不會。

外觀模式

講互相有依賴關系的對象,統(tǒng)一放入一個新的外觀類中,已去除彼此間的依賴,降低耦合。類似于OrderTO中的bizOrder,payOrder以及l(fā)ogicOrder。

橋接模式

通過對Bridge類的調用,實現(xiàn)了對接口Sourceable的實現(xiàn)類SourceSub1和SourceSub2的調用


Paste_Image.png
組合模式

將多個對象組合在一起進行操作,常用于表示樹形結構中,例如二叉樹

Paste_Image.png
享元模式

享元模式的主要目的是實現(xiàn)對象的共享,即共享池,當系統(tǒng)中對象多的時候可以減少內存的開銷,通常與工廠模式一起使用。

策略模式

策略模式定義了一系列算法,并將每個算法封裝起來,使他們可以相互替換,且算法的變化不會影響到使用算法的客戶。需要設計一個接口,為一系列實現(xiàn)類提供統(tǒng)一的方法,多個實現(xiàn)類實現(xiàn)該接口,設計一個抽象類(可有可無,屬于輔助類),提供輔助函數(shù)。

模板模式

一個抽象類中,有一個主方法,再定義1...n個方法,可以是抽象的,也可以是實際的方法,定義一個類,繼承該抽象類,重寫抽象方法,通過調用抽象類,實現(xiàn)對子類的調用

觀察者模式

當一個對象變化時,其它依賴該對象的對象都會收到通知,并且隨著變化!對象之間是一種一對多的關系

責任鏈模式

有多個對象,每個對象持有對下一個對象的引用,這樣就會形成一條鏈,請求在這條鏈上傳遞,直到某一對象決定處理該請求。但是發(fā)出者并不清楚到底最終那個對象會處理該請求,所以,責任鏈模式可以實現(xiàn),在隱瞞客戶端的情況下,對系統(tǒng)進行動態(tài)的調整

狀態(tài)模式

根據(jù)狀態(tài)的不同來實現(xiàn)不同的業(yè)務處理

HashMap的實現(xiàn)

HashMap基于數(shù)組+鏈表實現(xiàn),其中數(shù)組是key值對應的hashcode,而鏈表則是對應的entry,entry是key-value的組合,根據(jù)key的hashcode決定存儲在哪個位置對應的鏈表中。Entry還包含next屬性,指向下一個entry對象。
在遍歷hashMap時,如果需要remove某個位置的參數(shù),要用iterator來remove,直接調用hashmap的remove方法會導致modCount變化,modCount不匹配時會報出ConcurrentModifitionException,導致remove失敗。
key值的hashcode對應的鏈表需要去重排元素。

ConcurrentHashMap主要引入了segment,把整個hashmap分段加鎖,來達到控制并發(fā)同時解決單一鎖瓶頸的目的。segment繼承了ReentrantLock,另一種鎖機制。

Volatile

每個線程都有自己的線程??臻g,不存在共享資源的問題。
基礎類型的變量存儲在棧中,復雜對象的引用保存在線程棧中,而對應的對象則存儲在堆中。
volatile只能修飾變量,而synchronized則可以修飾類、方法、代碼塊、變量,作用域是不同的

  • 針對基礎類型

    • 對于沒有使用volatile修飾的基礎類型,每個線程在用到這個對象時,都會從主線程的??臻g中拷貝一份副本到本線程棧中,后面的每次操作都是操作這個副本。只有當線程結束時,會同步給主線程空間。
    • 而對于使用volatile修飾的基礎類型,則每個線程操作的都是主線程棧中的變量,但是jvm虛擬機只是保證從主內存加載到線程工作內存的值是最新的,還是會存在并發(fā)問題。
  • 對于引用變量
    不管是否使用volatile,線程棧中保存的都是一個引用指針,保存的是堆中的內存地址,指向的是堆中的對象。所以這種情況下的讀寫操作,沒辦法保證是原子性的。

IO

阻塞IO
非阻塞IO
SELECTOR

concurrent包下的常用類
jvm,類加載
常見的代碼,字符串,字符
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,638評論 18 399
  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,534評論 19 139
  • 一、設計模式的分類 總體來說設計模式分為三大類: 創(chuàng)建型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者...
    RamboLI閱讀 827評論 0 1
  • 2017.02.25 周六 陰 閱讀這篇文章大概需要8分鐘。 一 周末閑暇無事,橙子不知道去哪浪了,波波去修雙學位...
    盧安克閱讀 1,167評論 13 3
  • 其實很多選擇自己的發(fā)展方向包括擇偶標準都是價值觀使然,也沒有什么好遺憾的。最近在研究一本關于說話的書,挺有意思的。...
    六月花閱讀 218評論 0 0

友情鏈接更多精彩內容