OSI7層協議模型 html
TCP和UDP是OSI模型中的運輸層中的協議。TCP提供可靠的通訊傳輸,而UDP則常被用於廣播和細節控制交給應用的通訊傳輸前端
參考:http://www.javashuo.com/article/p-cmozunyh-ma.htmljava
一、TCP面向鏈接(如打電話要先撥號創建鏈接);UDP是無鏈接的,即發送數據以前不須要創建鏈接linux
二、TCP提供可靠的服務(3次握手)。經過TCP鏈接傳送的數據,無差錯,不丟失,不重複,且按序到達;程序員
UDP盡最大努力交付,即不保證可靠交付redis
TCP經過校驗和,重傳控制,序號標識,滑動窗口、確認應答實現可靠傳輸。如丟包時的重發控制,還能夠對次序亂掉的分包進行順序控制。算法
三、UDP具備較好的實時性,工做效率比TCP高,適用於對高速傳輸和實時性有較高的通訊或廣播通訊。spring
四、每一條TCP鏈接只能是點到點的;UDP支持一對一,一對多,多對一和多對多的交互通訊sql
五、TCP對系統資源要求較多,UDP對系統資源要求較少。數據庫
RTT:發送一個數據包到收到對應的ACK,所花費的時間
RTO:重傳時間間隔
TCP使用滑動窗口作流量控制與亂序重排
所謂滑動窗口協議,本身理解有兩點:
1. 「窗口」對應的是一段能夠被髮送者發送的字節序列,其連續的範圍稱之爲「窗口」;
2. 「滑動」則是指這段「容許發送的範圍」是能夠隨着發送的過程而變化的,方式就是按順序「滑動」。
在引入一個例子來講這個協議以前,我以爲頗有必要先了解如下前提:
-1. TCP協議的兩端分別爲發送者A和接收者B,因爲是全雙工協議,所以A和B應該分別維護着一個獨立的發送緩衝區和接收緩衝區,因爲對等性(A發B收和B發A收),咱們以A發送B接收的狀況做爲例子;
-2. 發送窗口是發送緩存中的一部分,是能夠被TCP協議發送的那部分,其實應用層須要發送的全部數據都被放進了發送者的發送緩衝區;
-3. 發送窗口中相關的有四個概念:已發送並收到確認的數據(再也不發送窗口和發送緩衝區以內)、已發送但未收到確認的數據(位於發送窗口之中)、容許發送但還沒有發送的數據以及發送窗口外發送緩衝區內暫時不容許發送的數據;
-4. 每次成功發送數據以後,發送窗口就會在發送緩衝區中按順序移動,將新的數據包含到窗口中準備發送;
核心對象是XMLHttpRequest,它能夠提供不從新加載頁面的狀況下更新網頁,在頁面加載後在客戶端向服務器請求數據,在頁面加載後在服務器端接受數據,在後臺向客戶端發送數據。XMLHttpRequest 對象提供了對 HTTP 協議的徹底的訪問,包括作出 POST 和 HEAD 請求以及普通的 GET 請求的能力。XMLHttpRequest 能夠同步或異步返回 Web 服務器的響應,而且能以文本或者一個 DOM 文檔形式返回內容。儘管名爲 XMLHttpRequest,它並不限於和 XML 文檔一塊兒使用:它能夠接收任何形式的文本文檔。XMLHttpRequest 對象是名爲 AJAX 的 Web 應用程序架構的一項關鍵功能
抽象:父類爲子類提供一些屬性和行爲,子類根據業務需求實現具體的行爲。抽象類使用abstract進行修飾,子類要實現全部的父類抽象方法不然子類也是抽象類。
封裝:封裝能夠隱藏類的內部屬性,而且對用戶隱藏了數據的訪問方式,這樣能夠保護類的內部狀態。封裝能夠防止類中的方法訪問屬性,防止對象間的交互,提升Java程序的安全性。
將類封裝起來,不可爲外部訪問,能夠防止開發人員浪費沒必要要的精力。
繼承:子類繼承父類的屬性和行爲,並能根據本身的需求擴展出新的屬性和行爲,提升了代碼的可複用性。
多態:不修改程序代碼就能夠改變程序運行時所綁定的具體代碼,讓程序能夠選擇多個運行狀態;具體的實現方式就是:接口實現,繼承父類進行方法重寫,同一個類中進行方法重載。
JAVA反射機制是在運行狀態中,
對於任意一個類,都可以知道這個類的全部屬性和方法;
對於任意一個對象,都可以調用它的任意一個方法和屬性;
這種動態獲取的信息以及動態調用對象的方法的功能稱爲java語言的反射機制。
參考大佬的文章,寫的不錯:
http://www.javashuo.com/article/p-fwuoucep-dx.html
參考:
http://www.cnblogs.com/lulipro/p/7504267.html
Java 異常的基類爲 java.lang.Throwable,java.lang.Error 和 java.lang.Exception 繼承 Throwable。
錯誤:Error類以及他的子類的實例,表明了JVM自己的錯誤。錯誤不能被程序員經過代碼處理,Error不多出現。所以,程序員應該關注Exception爲父類的分支下的各類異常類。
異常:Exception以及他的子類,表明程序運行時發送的各類不指望發生的事件。能夠被Java異常處理機制使用,是異常處理的核心。
非檢查異常(unckecked exception):Error 和 RuntimeException 以及他們的子類。javac在編譯時,不會提示和發現這樣的異常,不要求在程序處理這些異常。因此若是願意,咱們能夠編寫代碼處理(使用try...catch...finally)這樣的異常,也能夠不處理。對於這些異常,咱們應該修正代碼,而不是去經過異常處理器處理 。這樣的異常發生的緣由多半是代碼寫的有問題。如除0錯誤ArithmeticException,錯誤的強制類型轉換錯誤ClassCastException,數組索引越界ArrayIndexOutOfBoundsException,使用了空對象NullPointerException等等。
檢查異常(checked exception):除了Error 和 RuntimeException的其它異常。javac強制要求程序員爲這樣的異常作預備處理工做(使用try...catch...finally或者throws)。在方法中要麼用try-catch語句捕獲它並處理,要麼用throws子句聲明拋出它,不然編譯不會經過。這樣的異常通常是由程序的運行環境致使的。由於程序可能被運行在各類未知的環境下,而程序員沒法干預用戶如何使用他編寫的程序,因而程序員就應該爲這樣的異常時刻準備着。如SQLException , IOException,ClassNotFoundException 等。
異常處理:在編寫代碼處理異常時,對於檢查異常,有2種不一樣的處理方式:使用try...catch...finally語句塊處理它。或者,在函數簽名中使用throws 聲明交給函數調用者去解決。
答:會執行,在方法返回調用者前執行。Java 容許在 finally 中改變返回值的作法是很差的,由於若是存在 finally 代碼塊,try 中的 return 語句不會立馬返回調用者,而是記錄下返回值待 finally 代碼塊執行完畢以後再向調用者返回其值,而後若是在 finally 中修改了返回值,這會對程序形成很大的困擾。
NullPointerException:當操做一個空引用時會出現此錯誤。
NumberFormatException:數據格式轉換出現問題時出現此異常。
ClassCastException:強制類型轉換類型不匹配時出現此異常。
ArrayIndexOutOfBoundsException:數組下標越界,當使用一個不存在的數組下標時出現此異常。
ArithmeticException:數學運行錯誤時出現此異常
IllegalArgumentException:不合法的參數異常
抽象類:
在瞭解抽象類以前,先來了解一下抽象方法。抽象方法是一種特殊的方法:它只有聲明,而沒有具體的實現。抽象方法的聲明格式爲:
abstract void fun();
抽象方法必須用abstract關鍵字進行修飾。若是一個類含有抽象方法,則稱這個類爲抽象類,抽象類必須在類前用abstract關鍵字修飾。由於抽象類中含有無具體實現的方法,因此不能用抽象類建立對象。抽象類是用來捕捉子類的通用特性的 。它不能被實例化,只能被用做子類的超類。抽象類是被用來建立繼承層級裏子類的模板。
包含抽象方法的類稱爲抽象類,但並不意味着抽象類中只能有抽象方法,它和普通類同樣,一樣能夠擁有成員變量和普通的成員方法。注意,抽象類和普通類的主要有三點區別:
1)抽象方法必須爲public或者protected(由於若是爲private,則不能被子類繼承,子類便沒法實現該方法),缺省狀況下默認爲public。
2)抽象類不能用來建立對象;
3)若是一個類繼承於一個抽象類,則子類必須實現父類的抽象方法。若是子類沒有實現父類的抽象方法,則必須將子類也定義爲爲abstract類。
在其餘方面,抽象類和普通的類並無區別。
接口:
在軟件工程中,接口泛指供別人調用的方法或者函數,它是對行爲的抽象。
在Java中,定一個接口的形式以下:
[public] interface InterfaceName { }
接口是抽象方法的集合。若是一個類實現了某個接口,那麼它就繼承了這個接口的抽象方法。這就像契約模式,若是實現了這個接口,那麼就必須確保使用這些方法。接口只是一種形式,接口自身不能作任何事情。
要讓一個類遵循某組特定的接口須要使用implements關鍵字,具體格式以下:
class ClassName implements Interface1,Interface2,[....]{ }
區別:
1.語法層面上的區別
1)抽象類能夠提供成員方法的實現細節,而接口中只能存在public abstract 方法;
2)抽象類中的成員變量能夠是各類類型的,而接口中的成員變量只能是public static final類型的;
3)抽象類能夠有靜態代碼塊和靜態方法,而接口中不能含有靜態代碼塊以及靜態方法;
4)一個類只能繼承一個抽象類,而一個類卻能夠實現多個接口。
數組,鏈表,哈希表。各有優劣,順便提一下,數組連續內存空間,查找速度快,增刪慢;鏈表充分利用了內存,存儲空間是不連續的,首尾存儲上下一個節點的信息,因此尋址麻煩,查找速度慢,可是增刪快;哈希表呢,綜合了它們兩個的有點,一個哈希表,由數組和鏈表組成。假設一條鏈表有1000個節點,如今查找最後一個節點,就得從第一個遍歷到最後一個;若是用哈希表,將這條鏈表分爲10組,用一個容量爲10數組來存儲這10組鏈表的頭結點(a[0] = 0 , a[1] = 100 , a[2] = 200 …)。這樣尋址就快了。
參考:https://blog.csdn.net/HHcoco/article/details/53117525
題例:遍歷一個List有哪些不一樣的方式?
List<String> strList = new ArrayList<>(); //使用for-each循環 for(String obj : strList){ System.out.println(obj); } //using iterator迭代(使用迭代器更加線程安全。因爲它可以確保,在當前遍歷的集合元素被更改的時候。它會拋出ConcurrentModificationException。) Iterator<String> it = strList.iterator(); while(it.hasNext()){ String obj = it.next(); System.out.println(obj); }
HashMap
參考:http://youzhixueyuan.com/the-underlying-structure-and-principle-of-hashmap.html
從結構實現來說,HashMap是:數組+鏈表+紅黑樹(JDK1.8增長了紅黑樹部分)實現的,以下如所示。
(1) HashMap:它根據鍵的hashCode值存儲數據,大多數狀況下能夠直接定位到它的值,於是具備很快的訪問速度,但遍歷順序倒是不肯定的。
HashMap最多隻容許一條記錄的鍵爲null,容許多條記錄的值爲null。HashMap非線程安全,即任一時刻能夠有多個線程同時寫HashMap,可能會致使數據的不一致。若是須要知足線程安全,能夠用 Collections的synchronizedMap方法使HashMap具備線程安全的能力,或者使用ConcurrentHashMap。
(2) Hashtable:Hashtable是遺留類,不少映射的經常使用功能與HashMap相似,不一樣的是它承自Dictionary類,而且是線程安全的,任一時間只有一個線程能寫Hashtable,併發性不如ConcurrentHashMap,由於ConcurrentHashMap引入了分段鎖。Hashtable不建議在新代碼中使用,不須要線程安全的場合能夠用HashMap替換,須要線程安全的場合能夠用ConcurrentHashMap替換。
(3) LinkedHashMap:LinkedHashMap是HashMap的一個子類,保存了記錄的插入順序,在用Iterator遍歷LinkedHashMap時,先獲得的記錄確定是先插入的,也能夠在構造時帶參數,按照訪問次序排序。
(4) TreeMap:TreeMap實現SortedMap接口,可以把它保存的記錄根據鍵排序,默認是按鍵值的升序排序,也能夠指定排序的比較器,當用Iterator遍歷TreeMap時,獲得的記錄是排過序的。若是使用排序的映射,建議使用TreeMap。在使用TreeMap時,key必須實現Comparable接口或者在構造TreeMap傳入自定義的Comparator,不然會在運行時拋出java.lang.ClassCastException類型的異常。
當hashmap中的元素個數超過數組大小*loadFactor時,就會進行數組擴容,loadFactor的默認值爲0.75,也就是說,默認狀況下,數組大小爲16,那麼當hashmap中元素個數超過16*0.75=12的時候,就把數組的大小擴展爲2*16=32,即擴大一倍,而後從新計算每一個元素在數組中的位置,而這是一個很是消耗性能的操做,因此若是咱們已經預知hashmap中元素的個數,那麼預設元素的個數可以有效的提升hashmap的性能。好比說,咱們有1000個元素new HashMap(1000), 可是理論上來說new HashMap(1024)更合適,不過上面annegu已經說過,即便是1000,hashmap也自動會將其設置爲1024。 可是new HashMap(1024)還不是更合適的,由於0.75*1000 < 1000, 也就是說爲了讓0.75 * size > 1000, 咱們必須這樣new HashMap(2048)才最合適,既考慮了&的問題,也避免了resize的問題。
(1) for each map.entrySet()
Map<String, String> map = new HashMap<String, String>(); for (Entry<String, String> entry : map.entrySet()) { entry.getKey(); entry.getValue(); }
(2) 顯示調用map.entrySet()的集合迭代器
Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<String, String> entry = iterator.next(); entry.getKey(); entry.getValue(); }
(3) for each map.keySet(),再調用get獲取
Map<String, String> map = new HashMap<String, String>(); for (String key : map.keySet()) { map.get(key); }
(4) for each map.entrySet(),用臨時變量保存map.entrySet()
Set<Entry<String, String>> entrySet = map.entrySet(); for (Entry<String, String> entry : entrySet) { entry.getKey(); entry.getValue(); }
怎麼解決哈希衝突
開放定地址法:當衝突發生時,使用某種探查(亦稱探測)技術在散列表中造成一個探查(測)序列。
沿此序列逐個單元地查找,直到找到給定 的關鍵字,或者碰到一個開放的地址(即該地址單元爲空)爲止(若要插入,在探查到開放的地址,則可將待插入的新結點存人該地址單元)。
查找時探查到開放的 地址則代表表中無待查的關鍵字,即查找失敗。
鏈地址法:將哈希值相同的元素構成一個同義詞的單鏈表,並將單鏈表的頭指針存放在哈希表的第i個單元中,查找、插入和刪除主要在同義詞鏈表中進行。
鏈表法適用於常常進行插入和刪除的狀況。
在java中,連接地址法也是HashMap解決哈希衝突的方法之一,jdk1.7徹底採用單鏈表來存儲同義詞,jdk1.8則採用了一種混合模式,對於鏈表長度大於8的,會轉換爲紅黑樹存儲。
HashMap和Hashtable
首先來看HashMap和HashTable,這兩兄弟常常被放到一塊兒來比較,那麼它們有什麼不同呢?
a.HashMap不是線程安全的;HashTable是線程安全的,其線程安全是經過Sychronize實現。
b.因爲上述緣由,HashMap效率高於HashTable。
c.HashMap的鍵能夠爲null,HashTable不能夠。
d.多線程環境下,一般也不是用HashTable,由於效率低。HashMap配合Collections工具類使用實現線程安全。同時還有ConcurrentHashMap能夠選擇,該類的線程安全是經過Lock的方式實現的,因此效率高於Hashtable。
ArrayList , LinkedList , Vector
a.ArrayList和Vector本質都是用數組實現的,而LinkList是用雙鏈表實現的;因此,Arraylist和Vector在查找效率上比較高,增刪效率比較低;LinkedList則正好相反。
b.ArrayList是線程不安全的,Vector是線程安全的,效率確定沒有ArrayList高了。實際中通常也不怎麼用Vector,能夠本身作線程同步,也能夠用Collections配合ArrayList實現線程同步。
HashSet和TreeSet
Set集合類的特色就是能夠去重,它們的內部實現都是基於Map的,用的是Map的key.
一、TreeSet 是二差樹實現的,Treeset中的數據是自動排好序的,不容許放入null值。
二、HashSet 是哈希表實現的,HashSet中的數據是無序的,能夠放入null,但只能放入一個null,二者中的值都不能重複,就如數據庫中惟一約束。
三、比較方法基於hascode()方法和equals()方法
IOC 控制反轉:是指容器控制程序對象之間的關係,而不是傳統實現中,由程序代碼直接操控。控制權由應用代碼中轉到了外部容器,控制權的轉移是所謂反轉。
對於Spring而言,就是由Spring來控制對象的生命週期和對象之間的關係;IoC還有另一個名字——「依賴注入(Dependency Injection)」。
從名字上理解,所謂依賴注入,即組件之間的依賴關係由容器在運行期決定,即由容器動態地將某種依賴關係注入到組件之中。
AOP 面向切面編程:利用一種稱爲「橫切」的技術,剖解開封裝的對象內部,並將那些影響了 多個類的公共行爲封裝到一個可重用模塊,並將其名爲「Aspect」,即方面。
所謂「方面」,簡單地說,就是將那些與業務無關,卻爲業務模塊所共同調用的 邏輯或責任封裝起來,
好比日誌記錄,便於減小系統的重複代碼,下降模塊間的耦合度,並有利於將來的可操做性和可維護性。
一、實例化一個Bean--也就是咱們常說的new;
二、按照Spring上下文對實例化的Bean進行配置--也就是IOC注入;
三、若是這個Bean已經實現了BeanNameAware接口,會調用它實現的setBeanName(String)方法,此處傳遞的就是Spring配置文件中Bean的id值
四、若是這個Bean已經實現了BeanFactoryAware接口,會調用它實現的setBeanFactory(setBeanFactory(BeanFactory)傳遞的是Spring工廠自身(能夠用這個方式來獲取其它Bean,只需在Spring配置文件中配置一個普通的Bean就能夠);
五、若是這個Bean已經實現了ApplicationContextAware接口,會調用setApplicationContext(ApplicationContext)方法,傳入Spring上下文(一樣這個方式也能夠實現步驟4的內容,但比4更好,由於ApplicationContext是BeanFactory的子接口,有更多的實現方法);
六、若是這個Bean關聯了BeanPostProcessor接口,將會調用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor常常被用做是Bean內容的更改,而且因爲這個是在Bean初始化結束時調用那個的方法,也能夠被應用於內存或緩存技術;
七、若是Bean在Spring配置文件中配置了init-method屬性會自動調用其配置的初始化方法。
八、若是這個Bean關聯了BeanPostProcessor接口,將會調用postProcessAfterInitialization(Object obj, String s)方法、;
注:以上工做完成之後就能夠應用這個Bean了,那這個Bean是一個Singleton的,因此通常狀況下咱們調用同一個id的Bean會是在內容地址相同的實例,固然在Spring配置文件中也能夠配置非Singleton,這裏咱們不作贅述。
九、當Bean再也不須要時,會通過清理階段,若是Bean實現了DisposableBean這個接口,會調用那個其實現的destroy()方法;
十、最後,若是這個Bean的Spring配置中配置了destroy-method屬性,會自動調用其配置的銷燬方法。
題例:@autowired和@resource的區別
一、 @Autowired與@Resource均可以用來裝配bean. 均可以寫在字段上,或寫在setter方法上。
二、 @Autowired默認按類型裝配(這個註解是屬業spring的),默認狀況下必需要求依賴對象必須存在,若是要容許null值,能夠設置它的required屬性爲false,如:@Autowired(required=false) ,若是咱們想使用名稱裝配能夠結合@Qualifier註解進行使用,以下:
@Autowired @Qualifier("baseDao") privateBaseDao baseDao;
三、@Resource(這個註解屬於J2EE的),默認按照名稱進行裝配,名稱能夠經過name屬性進行指定,若是沒有指定name屬性,當註解寫在字段上時,默認取字段名進行安裝名稱查找,若是註解寫在setter方法上默認取屬性名進行裝配。當找不到與名稱匹配的bean時才按照類型進行裝配。可是須要注意的是,若是name屬性一旦指定,就只會按照名稱進行裝配。
@Resource(name="baseDao") privateBaseDao baseDao;
什麼是 @RestController /* @Controller + @ResponseBody*/
@Controller 只是定義了一個控制器類
@RequestMapping 是一個用來處理請求地址映射的註解,可用於類或方法上。
@Responsebody 註解表示該方法的返回的結果直接寫入 HTTP 響應正文(ResponseBody)中,通常在異步獲取數據時使用,一般是在使用 @RequestMapping 後,返回值一般解析爲跳轉路徑,加上 @Responsebody 後返回結果不會被解析爲跳轉路徑,而是直接寫入HTTP 響應正文中。
做用:
該註解用於將Controller的方法返回的對象,經過適當的HttpMessageConverter轉換爲指定格式後,寫入到Response對象的body數據區。
使用時機:
返回的數據不是html標籤的頁面,而是其餘某種格式的數據時(如json、xml等)使用;
Spring框架中使用到了大量的設計模式,下面列舉了比較有表明性的:
代理模式—在AOP和remoting中被用的比較多。
單例模式—在spring配置文件中定義的bean默認爲單例模式。
模板方法—用來解決代碼重複的問題。好比. RestTemplate, JmsTemplate
, JpaTemplate。
工廠模式—BeanFactory用來建立對象的實例。
適配器–spring aop
裝飾器–spring data hashmapper
觀察者– spring 時間驅動模型
回調–Spring ResourceLoaderAware回調接口
前端控制器–spring用前端控制器DispatcherServlet對請求進行分發
1.遠程過程調用(REST、RPC、Apache Thrift)
優勢:
2.消息(RabbitMQ、ActiveMQ、kafka)
優勢:
缺點:
事務必須服從ACID原則。ACID指的是原子性(atomicity)、一致性(consistency)、隔離性(isolation)和持久性(durability)。 通俗理解,事務其實就是一系列指令的集合。
線程是操做系統可以進行運算調度的最小單位,線程是進程的子集,一個進程能夠有不少線程,每條線程並行執行不一樣的任務。
不一樣的進程使用不一樣的內存空間,而全部的線程共享一片相同的內存空間。別把它和棧內存搞混,每一個線程都擁有單獨的棧內存用來存儲本地數據。
參考:http://www.javashuo.com/article/p-nhfryjed-cg.html
例題:如何在java中建立線程
java.lang.Thread 類的實例就是一個線程可是它須要調用java.lang.Runnable接口來執行,因爲線程類自己就是調用的Runnable接口。因此你能夠
一、繼承java.lang.Thread 類
二、直接調用Runnable接口來重寫run()方法實現線程
三、實現Callable接口經過FutureTask包裝器來建立Thread線程
Java不支持類的多重繼承,但容許你調用多個接口。因此若是你要繼承其餘類,固然是調用Runnable接口好於繼承Thread類
線程同步的方法
1.同步方法 2.同步代碼塊 3.wait與notify 4.使用特殊域變量(volatile)實現線程同步 5.使用重入鎖實現線程同步 6.使用局部變量實現線程同步 7.使用阻塞隊列實現線程同步
sleep()和wait()有什麼區別?
sleep是線程類(Thread)的方法,致使此線程暫停執行指定時間,給執行機會給其餘線程,可是監控狀態依然保持,到時後會自動恢復。調用sleep不會釋放對象鎖。
wait是Object類的方法,對此對象調用wait方法致使本線程放棄對象鎖,進入等待此對象的等待鎖定池,只有針對此對象發出notify方法(或notifyAll)後本線程才進入對象鎖定池準備得到對象鎖進入運行狀態。
start()方法被用來啓動新建立的線程,並且start()內部調用了run()方法,這和直接調用run()方法的效果不同。當你調用run()方法的時候,只會是在原來的線程中調用,沒有新的線程啓動,start()方法纔會啓動新線程。
1.調用star()方法會建立一個新的子線程並啓動
2.run()方法只是Thread的一個普通方法的調用
Thread和rRunnabe區別
Thread是實現了Runnable接口的類,使得run()支持多線程
BIO/NIO
BIO:同步阻塞式IO,服務器實現模式爲一個鏈接一個線程,即客戶端有鏈接請求時服務器端就須要啓動一個線程進行處理,若是這個鏈接不作任何事情會形成沒必要要的線程開銷,固然能夠經過線程池機制改善。
NIO:同步非阻塞式IO,服務器實現模式爲一個請求一個線程,即客戶端發送的鏈接請求都會註冊到多路複用器上,多路複用器輪詢到鏈接有I/O請求時才啓動一個線程進行處理。
AIO(NIO.2):異步非阻塞式IO,服務器實現模式爲一個有效請求一個線程,客戶端的I/O請求都是由OS先完成了再通知服務器應用去啓動線程進行處理。
說明:
進程是資源分配的最小單位,線程是CPU調度的最小單位。
1.運行一個程序會包含一個進程,一個進程至少包含一個線程,每一個線程包含一個子任務
2.全部與進程有關的資源都被記錄在PCB中
3.進程是搶佔處理機的調度單位;每一個進程對應一個JVM實例,線程屬於某個進程,共享其資源(JVM裏的堆)
4.線程只由堆、棧、程序計數器和TCB(線程控制表)組成
5.java採用單線程編程模型,程序會自動建立主線程,主線程能夠建立子線程,原則上要後於子線程完成執行
區別:
1.線程不能看作獨立應用,而進程能夠看作獨立應用
2.進程有獨立的地址空間,相互不影響,線程只是進程的不一樣執行路徑
3.線程沒有獨立的地址空間,多進程的程序比多進程的程序健壯
4.進程切換比線程切花開銷大
線程池是指在初始化一個多線程應用程序過程當中建立一個線程集合,而後在須要執行新的任務時重用這些線程而不是新建一個線程。線程池中線程的數量一般徹底取決於可用內存數量和應用程序的需求。然而,增長可用線程數量是可能的。線程池中的每一個線程都有被分配一個任務,一旦任務已經完成了,線程回到池子中並等待下一次分配任務。
建立線程池的方式
Java經過Executors提供四種線程池,分別爲:
newCachedThreadPool 建立一個可緩存線程池,若是線程池長度超過處理須要,可靈活回收空閒線程,若無可回收,則新建線程。
newFixedThreadPool 建立一個定長線程池,可控制線程最大併發數,超出的線程會在隊列中等待。
newScheduledThreadPool 建立一個定長線程池,支持定時及週期性任務執行。
newSingleThreadExecutor 建立一個單線程化的線程池,它只會用惟一的工做線程來執行任務,保證全部任務按照指定順序(FIFO, LIFO, 優先級)執行。
參考:http://www.javashuo.com/article/p-tgchfxdn-ky.html
線程池優勢
1)重用存在的線程,減小對象建立銷燬的開銷。
2)可有效的控制最大併發線程數,提升系統資源的使用率,同時避免過多資源競爭,避免堵塞。
3)提供定時執行、按期執行、單線程、併發數控制等功能。
在併發編程中,常常遇到多個線程訪問同一個 共享資源 ,這時候做爲開發者必須考慮如何維護數據一致性,在java中synchronized關鍵字被經常使用於維護數據一致性。synchronized機制是給共享資源上鎖,只有拿到鎖的線程才能夠訪問共享資源,這樣就能夠強制使得對共享資源的訪問都是順序的,由於對於共享資源屬性訪問是必要也是必須的
悲觀鎖:老是假設最壞的狀況,每次去拿數據的時候都認爲別人會修改,因此每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會阻塞直到它拿到鎖。傳統的關係型數據庫裏邊就用到了不少這種鎖機制,好比行鎖,表鎖等,讀鎖,寫鎖等,都是在作操做以前先上鎖。再好比Java裏面的同步原語synchronized關鍵字的實現也是悲觀鎖。
樂觀鎖:顧名思義,就是很樂觀,每次去拿數據的時候都認爲別人不會修改,因此不會上鎖,可是在更新的時候會判斷一下在此期間別人有沒有去更新這個數據,可使用版本號等機制。樂觀鎖適用於多讀的應用類型,這樣能夠提升吞吐量,像數據庫提供的相似於write_condition機制,其實都是提供的樂觀鎖。在Java中java.util.concurrent.atomic包下面的原子變量類就是使用了樂觀鎖的一種實現方式CAS實現的。
CAS算法:即compare and swap(比較與交換),是一種有名的無鎖算法。無鎖編程,即不使用鎖的狀況下實現多線程之間的變量同步,也就是在沒有線程被阻塞的狀況下實現變量的同步,因此也叫非阻塞同步(Non-blocking Synchronization)。CAS算法涉及到三個操做數
分佈式鎖的幾種實現方式(redis、zookeeper、數據庫)
1.基於數據庫實現分佈式鎖:
在數據庫中建立一個表,表中包含方法名等字段,並在方法名字段上建立惟一索引,想要執行某個方法,就使用這個方法名向表中插入數據,成功插入則獲取鎖,執行完成後刪除對應的行數據釋放鎖。
2.基於緩存實現分佈式鎖 :
(1)獲取鎖的時候,使用setnx加鎖,並使用expire命令爲鎖添加一個超時時間,超過該時間則自動釋放鎖,鎖的value值爲一個隨機生成的UUID,經過此在釋放鎖的時候進行判斷。
(2)獲取鎖的時候還設置一個獲取的超時時間,若超過這個時間則放棄獲取鎖。
(3)釋放鎖的時候,經過UUID判斷是否是該鎖,如果該鎖,則執行delete進行鎖釋放。
3.基於Zookeeper實現分佈式鎖:
ZooKeeper是一個爲分佈式應用提供一致性服務的開源組件,它內部是一個分層的文件系統目錄樹結構,規定同一個目錄下只能有一個惟一文件名。基於ZooKeeper實現分佈式鎖的步驟以下:
(1)建立一個目錄mylock;
(2)線程A想獲取鎖就在mylock目錄下建立臨時順序節點;
(3)獲取mylock目錄下全部的子節點,而後獲取比本身小的兄弟節點,若是不存在,則說明當前線程順序號最小,得到鎖;
(4)線程B獲取全部節點,判斷本身不是最小節點,設置監聽比本身次小的節點;
(5)線程A處理完,刪除本身的節點,線程B監聽到變動事件,判斷本身是否是最小的節點,若是是則得到鎖。
高併發下的線程安全實現方式:
互斥同步、非阻塞同步(CAS)、線程局部變量(threadLocal)、wait和notify、java.util.concurrent併發工具包、volatile保證變量的線程安全等
參考:http://www.javashuo.com/article/p-seevzcmg-dg.html
存儲過程(Stored Procedure)是在大型數據庫系統中,一組爲了完成特定功能的SQL 語句集,它存儲在數據庫中,一次編譯後永久有效,用戶經過指定存儲過程的名字並給出參數(若是該存儲過程帶有參數)來執行它。
觸發器(trigger)是SQL server 提供給程序員和數據分析員來保證數據完整性的一種方法,它是與表事件相關的特殊的存儲過程,它的執行不是由程序調用,也不是手工啓動,而是由事件來觸發,好比當對一個表進行操做( insert,delete, update)時就會激活它執行。觸發器常常用於增強數據的完整性約束和業務規則等。
紅黑樹(平衡二叉查找樹)是每一個節點都帶有顏色屬性的二叉查找樹,顏色或紅色或黑色。
在二叉查找樹強制通常要求之外,對於任何有效的紅黑樹咱們增長了以下的額外要求:
索引數據結構(B+樹)
B樹:
1.根節點至少包含2個孩子
2.樹中每一個節點最多包含有m個孩子(m>=2)
3.除根節點和葉節點外,其餘每一個節點至少有ceil(m/2)(ceil取上限的意思)個孩子
4.全部葉子節點都位於同一層
5.關鍵字信息,左邊要比右邊小;個數n知足 ceil(m/2)-1 <= n <= m -1;最左邊關鍵字>孩子的全部關鍵字,最右邊的關鍵字<孩子的全部關鍵字,中間的處於區間便可
B+樹:
1.非葉子節點的子樹指針與關鍵字個數相同
2.非葉子節點的子樹指針,指向關鍵字值的子樹
3.非葉子節點僅用來索引,數據都保存在葉子節點中
4.全部葉子節點均有一個鏈指針指向下一個葉子節點
優勢:
1.磁盤讀寫代價更低(存放的是索引)
2.查詢效率更加穩定(任何關鍵字的查詢必須走一條從根節點到葉子節點的路)
3.更有利於對數據庫的掃描(每一個葉子都是用指針連接起來的)
InnoDB 事務型數據庫的首選引擎,支持事務安全表(ACID),支持行鎖定和外鍵。MySQL 5.5.5 以後,InnoDB 做爲默認存儲引擎。
MyISAM 它不支持事務,也不支持外鍵,尤爲是訪問速度快,對事務完整性沒有要求或者以SELECT、INSERT爲主的應用基本均可以使用這個引擎來建立表。
每一個MyISAM在磁盤上存儲成3個文件,其中文件名和表名都相同,可是擴展名分別爲:
MyISAM的表還支持3種不一樣的存儲格式:
靜態表是默認的存儲格式。靜態表中的字段都是非變長字段,這樣每一個記錄都是固定長度的,這種存儲方式的優勢是存儲很是迅速,容易緩存,出現故障容易恢復;缺點是佔用的空間一般比動態表多。靜態表在數據存儲時會根據列定義的寬度定義補足空格,可是在訪問的時候並不會獲得這些空格,這些空格在返回給應用以前已經去掉。同時須要注意:在某些狀況下可能須要返回字段後的空格,而使用這種格式時後面到空格會被自動處理掉。
動態表包含變長字段,記錄不是固定長度的,這樣存儲的優勢是佔用空間較少,可是頻繁到更新刪除記錄會產生碎片,須要按期執行OPTIMIZE TABLE語句或myisamchk -r命令來改善性能,而且出現故障的時候恢復相對比較困難。
壓縮表由myisamchk工具建立,佔據很是小的空間,由於每條記錄都是被單獨壓縮的,因此只有很是小的訪問開支。
InnoDB 該存儲引擎提供了具備提交、回滾和崩潰恢復能力的事務安全。可是對比MyISAM引擎,寫的處理效率會差一些,而且會佔用更多的磁盤空間以保留數據和索引。
MEMORY 使用存在於內存中的內容來建立表。每一個memory表只實際對應一個磁盤文件,格式是.frm。memory類型的表訪問很是的快,由於它的數據是放在內存中的,而且默認使用HASH索引,可是一旦服務關閉,表中的數據就會丟失掉。
MERGE Merge存儲引擎是一組MyISAM表的組合,這些MyISAM表必須結構徹底相同,merge表自己並無數據,對merge類型的表能夠進行查詢,更新,刪除操做,這些操做其實是對內部的MyISAM表進行的。
慢SQL優化
Mysql的優化,大致能夠分爲三部分:索引的優化,sql語句的優化,表的優化
一條 SQL 語句執行的很慢,那是每次執行都很慢呢?仍是大多數狀況下是正常的,偶爾出現很慢呢?因此我以爲,咱們還得分如下兩種狀況來討論。
一、大多數狀況是正常的,只是偶爾會出現很慢的狀況。
a.數據庫在刷新髒頁面
當咱們要往數據庫插入一條數據、或者要更新一條數據的時候,咱們知道數據庫會在內存中把對應字段的數據更新了,可是更新以後,這些更新的字段並不會立刻同步持久化到磁盤中去,而是把這些更新的記錄寫入到 redo log 日記中去,等到空閒的時候,在經過 redo log 裏的日記把最新的數據同步到磁盤中去。
不過,redo log 裏的容量是有限的,若是數據庫一直很忙,更新又很頻繁,這個時候 redo log 很快就會被寫滿了,這個時候就沒辦法等到空閒的時候再把數據同步到磁盤的,只能暫停其餘操做,全身心來把數據同步到磁盤中去的,而這個時候,就會致使咱們平時正常的SQL語句忽然執行的很慢,因此說,數據庫在在同步數據到磁盤的時候,就有可能致使咱們的SQL語句執行的很慢了。
b.拿不到鎖
咱們要執行的這條語句,恰好這條語句涉及到的表,別人在用,而且加鎖了,咱們拿不到鎖,只能慢慢等待別人釋放鎖了。或者,表沒有加鎖,但要使用到的某個一行被加鎖了。
若是要判斷是否真的在等待鎖,咱們能夠用 show processlist這個命令來查看當前的狀態
二、在數據量不變的狀況下,這條SQL語句一直以來都執行的很慢。
sql優化通常步驟概要:
a.經過 show status 命令瞭解各類sql的執行頻率
b.定位執行效率較低的sql語句
c.經過explain分析低效sql的執行計劃
d.經過 show profile 分析sql
e.經過trace分析 優化器 如何選擇執行計劃
f.肯定問題並採起相應的優化措施
索引失效的情
1.列與列對比
某個表中,有兩列(id和c_id)都建了單獨索引,下面這種查詢條件不會走索引
select * from test where id=c_id;
這種狀況會被認爲還不如走全表掃描。
2.存在NULL值條件
3.NOT條件
4.LIKE通配符
5.條件上包括函數
6.複合索引前導列區分大
當複合索引前導列區分小的時候,咱們有INDEX SKIP SCAN,當前導列區分度大,且查後導列的時候,前導列的分裂會很是耗資源,執行計劃想,還不如全表掃描來的快,而後就索引失效了。
7.數據類型的轉換
redis性能:10W +QPS(每秒內查詢次數)
Redis支持五種數據類型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。
redis爲何能這麼快
1.redis徹底基於內存,絕大部分請求是純粹的內存操做,執行效率高。採用單進程單線程模型的key-value數據庫,將數據存儲在內存裏面,讀寫數據不會受硬盤I/O速率的限制。
2.數據結構簡單,對數據操做也簡單。不使用表,採用鍵值對數據結構,不須要預約義以及強制關聯其餘數據。
3.採用單線程
4.適用多路I/O複用模型,非阻塞I/O
多路I/O複用模型
FD(file descriptor)文件描述符:一個打開的文件經過惟一的描述符進行引用,該描述符是打開文件的元數據到文件自己的映射
傳統的阻塞I/O模型:當適用read/write對某一個文件進行讀寫時,若是當前的FD不可讀或不可寫,整個redis服務就不會對其餘的操做進行響應,致使整個服務不可用
多路I/O複用模型:若是一個I/O流進來,咱們就開啓一個進程處理這個I/O流。那麼假設如今有一百萬個I/O流進來,那咱們就須要開啓一百萬個進程一一對應處理這些I/O流。一百萬個進程,你的CPU佔有率會多高,這個實現方式及其的不合理。因此人們提出了I/O多路複用這個模型,一個線程,經過記錄I/O流的狀態來同時管理多個I/O,能夠提升服務器的吞吐能力。實現方式採用多路複用函數,epoll/kqueue/evport/select系統調用,監聽文件是否可讀或可寫
redis數據類型
字符串類型(string),散列類型(hash),列表類型(list),集合類型(set),有序集合類型(zset),用來作基數統計的算法(HyperLogLog) ,支持存儲地理位置信息(Geo)
從海量Key裏面查詢出某一固定前綴的Key
若是使用 Keys pattern (*表示區配全部)命令,猶豫一次性輸出海量數據會形成服務器卡頓,故使用另外一種方式。
SCAN cursor [MATCH patten] [COUNT count]
1.基於遊標的迭代器,須要基於上一次的遊標延續以前的迭代過程
2.以0做爲遊標開始一次新的迭代,直到命令返回遊標0完成一次遍歷
3.不保證每次執行都返回某個給定數量的元素,支持模糊查詢
4.一次返回的數量不可控,只能是大機率符合count參數
什麼是Redis持久化?Redis有哪幾種持久化方式?優缺點是什麼?
持久化就是把內存的數據寫到磁盤中去,防止服務宕機了內存數據丟失。
RDB(快照)持久化:保存某個時間點的全量數據快照(缺省狀況狀況下,Redis把數據快照存放在磁盤上的二進制文件中,文件名爲dump.rdb。能夠配置Redis的持久化策略,例如數據集中每N秒鐘有超過M次更新,就將數據寫入磁盤;或者你能夠手工調用命令SAVE或BGSAVE。)
SAVE:阻塞redis服務器進程,直到RDB文件被建立完成
BGSAVE:Fork出一個子進程來建立RDB文件,不阻塞服務器進程
AOF(Append-Only-File)持久化:保存寫狀態(備份數據庫接收到的指令)
1.記錄下除查詢之外的全部變動數據庫狀態的命令
2.以append的形式追加保存到AOF文件中(增量)
RDB-AOF混合持久化方式:BGSAVE作鏡像全量持久化,AOF作增量持久化
linux經常使用命令