不足的地方請大家多多指正企量,如有其它沒有想到的常問面試題請大家多多評論恕沫,一起成長,感謝!~
String可以被繼承嗎?
因為Sting是這樣定義的:public final class String extends Object甘晤,里邊有final關(guān)鍵字撮弧,所以不能被繼承。
接口能繼承接口嗎?
一個接口可以繼承另一個接口易核,一個抽象類可以實現(xiàn)一個接口匈织。
synchronized與static synchronized 的區(qū)別
synchronized是對類的當前實例進行加鎖,防止其他線程同時訪問該類的該實例的所有synchronized塊牡直,注意這里是“類的當前實例”缀匕, 類的兩個不同實例就沒有這種約束了。那么static synchronized恰好就是要控制類的所有實例的訪問了碰逸,static synchronized是限制線程同時訪問jvm中該類的所有實例同時訪問對應(yīng)的代碼快乡小。實際上,在類中某方法或某代碼塊中有 synchronized花竞,那么在生成一個該類實例后劲件,改類也就有一個監(jiān)視快,放置線程并發(fā)訪問改實例synchronized保護快约急,而static synchronized則是所有該類的實例公用一個監(jiān)視快了零远,也也就是兩個的區(qū)別了,也就是synchronized相當于 this.synchronized,而
static synchronized相當于Something.synchronized.
Spring BeanFactory與FactoryBean的區(qū)別
BeanFactory厌蔽,以Factory結(jié)尾牵辣,表示它是一個工廠類(接口),用于管理Bean的一個工廠奴饮。在Spring中纬向,BeanFactory是IOC容器的核心接口,它的職責包括:實例化戴卜、定位逾条、配置應(yīng)用程序中的對象及建立這些對象間的依賴。BeanFactory投剥,以Factory結(jié)尾师脂,表示它是一個工廠類(接口),用于管理Bean的一個工廠江锨。在Spring中吃警,BeanFactory是IOC容器的核心接口,它的職責包括:實例化啄育、定位酌心、配置應(yīng)用程序中的對象及建立這些對象間的依賴。
FactoryBean以Bean結(jié)尾挑豌,表示它是一個Bean安券,不同于普通Bean的是:它是實現(xiàn)了FactoryBean接口的Bean墩崩,根據(jù)該Bean的ID從BeanFactory中獲取的實際上是FactoryBean的getObject()返回的對象,而不是FactoryBean本身完疫,如果要獲取FactoryBean對象泰鸡,請在id前面加一個&符號來獲取。
線程有返回值嗎
在Java5之前壳鹤,線程是沒有返回值的盛龄,常常為了“有”返回值,破費周折芳誓,而且代碼很不好寫余舶。或者干脆繞過這道坎锹淌,走別的路了匿值。
現(xiàn)在Java終于有可返回值的任務(wù)(也可以叫做線程)了。
可返回值的任務(wù)必須實現(xiàn)Callable接口赂摆,類似的挟憔,無返回值的任務(wù)必須Runnable接口。
執(zhí)行Callable任務(wù)后烟号,可以獲取一個Future的對象绊谭,在該對象上調(diào)用get就可以獲取到Callable任務(wù)返回的Object了。
Volatile真的能解決線程并發(fā)嗎?
用volatile修飾的變量 是java 語言提供的一種稍弱的同步機制汪拥,線程每次操作前都從主內(nèi)存中刷新值达传,變量的更新操作也會及時的通知到其他線程。
如果把變量聲明成volatile 類型 編譯器和運行時都會注意變量值迫筑。
線程在每次使用變量的時候宪赶,都會讀取變量修改后的最的值。volatile很容易被誤用脯燃,用來進行原子性操作搂妻。
hashmap、concurrenthashmap底層實現(xiàn)和區(qū)別
Hashmap本質(zhì)是數(shù)組加鏈表辕棚。根據(jù)key取得hash值叽讳,然后計算出數(shù)組下標,如果多個key對應(yīng)到同一個下標坟募,就用鏈表串起來,新插入的在前面邑狸。
ConcurrentHashMap:在hashMap的基礎(chǔ)上懈糯,ConcurrentHashMap將數(shù)據(jù)分為多個segment,默認16個(concurrency level)单雾,然后每次操作對一個segment加鎖赚哗,避免多線程鎖的幾率她紫,提高并發(fā)效率。
hashmap概述
HashMap基于哈希表的 Map 接口的實現(xiàn)屿储。此實現(xiàn)提供所有可選的映射操作贿讹,并允許使用 null 值和 null 鍵。(除了不同步和允許使用 null 之外够掠,HashMap 類與 Hashtable 大致相同民褂。)此類不保證映射的順序,特別是它不保證該順序恒久不變疯潭。
值得注意的是HashMap不是線程安全的赊堪,如果想要線程安全的HashMap,可以通過Collections類的靜態(tài)方法synchronizedMap獲得線程安全的HashMap竖哩。
HashMap的數(shù)據(jù)結(jié)構(gòu)
HashMap的底層主要是基于數(shù)組和鏈表來實現(xiàn)的哭廉,它之所以有相當快的查詢速度主要是因為它是通過計算散列碼來決定存儲的位置,能夠很快的計算出對象所存儲的位置相叁。HashMap中主要是通過key的hashCode 來計算hash值的遵绰,只要hashCode相同,計算出來的hash值就一樣增淹。如果存儲的對象對多了椿访,就有可能不同的對象所算出來的hash值是相同的,這就出現(xiàn)了所謂的hash沖突埠通。學過數(shù)據(jù)結(jié)構(gòu)的同學都知道赎离,解決hash沖突的方法有很多,HashMap底層是通過鏈表來解決hash沖突的端辱。
HashMap的初始大小16個Entry,但是我在hashmap里面放了超過16個元素梁剔,擴容方法為resize()方法。
HashMap其實就是一個Entry數(shù)組舞蔽,Entry對象中包含了鍵和值荣病,其中next也是一個Entry對象,它就是用來處理hash沖突的渗柿,形成一個鏈表个盆。
鏈表長度到了一定的長度,存儲結(jié)構(gòu)就會變成紅黑樹
hashmap工作原理:
通過hash的方法朵栖,通過put和get存儲和獲取對象颊亮。存儲對象時,我們將K/V傳給put方法時陨溅,它調(diào)用hashCode計算hash從而得到bucket位置终惑,進一步存儲,HashMap會根據(jù)當前bucket的占用情況自動調(diào)整容量(超過Load Facotr則resize為原來的2倍)门扇。獲取對象時雹有,我們將K傳給get偿渡,它調(diào)用hashCode計算hash從而得到bucket位置,并進一步調(diào)用equals()方法確定鍵值對霸奕。如果發(fā)生碰撞的時候溜宽,Hashmap通過鏈表將產(chǎn)生碰撞沖突的元素組織起來,在Java 8中质帅,如果一個bucket中碰撞沖突的元素超過某個限制(默認是8)适揉,則使用紅黑樹來替換鏈表,從而提高速度临梗。
ConcurrentHashMap
ConcurrentHashMap具體是怎么實現(xiàn)線程安全的呢涡扼,肯定不可能是每個方法加synchronized,那樣就變成了HashTable盟庞。
從ConcurrentHashMap代碼中可以看出吃沪,它引入了一個“分段鎖”的概念,具體可以理解為把一個大的Map拆分成N個小的HashTable什猖,根據(jù)key.hashCode()來決定把key放到哪個HashTable中票彪。
在ConcurrentHashMap中,就是把Map分成了N個Segment不狮,put和get的時候降铸,都是現(xiàn)根據(jù)key.hashCode()算出放到哪個Segment中:
ConcurrentHashMap中默認是把segments初始化為長度為16的數(shù)組。
ConcurrentHashMap的工作機制摇零,通過把整個Map分為N個Segment(類似HashTable)推掸,可以提供相同的線程安全,但是效率提升N倍驻仅,默認提升16倍谅畅。
一:spring基本概念
1)struts2是web框架,hibernate是orm框架
2)spring是容器框架噪服,創(chuàng)建bean毡泻,維護bean之間的關(guān)系
3)spring可以管理web層,持久層粘优,業(yè)務(wù)層仇味,dao層,spring可以配置各個層的組件雹顺,并且維護各個層的關(guān)系
二:spring核心原理
1.IOC控制反轉(zhuǎn)
概念:控制權(quán)由對象本身轉(zhuǎn)向容器丹墨,由容器根據(jù)配置文件創(chuàng)建對象實例并實現(xiàn)各個對象的依賴關(guān)系。
核心:bean工廠
2.AOP面向切面編程
a.靜態(tài)代理
根據(jù)每個具體類分別編寫代理類
根據(jù)一個接口編寫一個代理類
b.動態(tài)代理
針對一個方面編寫一個InvocationHandler嬉愧,然后借用JDK反射包中的Proxy類為各種接口動態(tài)生成相應(yīng)的代理類
spring原理總結(jié)
1.使用spring 带到,沒有new對象,我們把創(chuàng)建對象的任務(wù)交給spring框架
2.spring實際上是一個容器框架,可以配置各種bean(action/service/domain/dao),并且可以維護bean與bean的關(guān)系,當我們需要使用某個bean的時候,我們可以getBean(id),使用即可.
jvm調(diào)優(yōu)(簡單概括)
1:建議用64位操作系統(tǒng)揽惹,Linux下64位的jdk比32位jdk要慢一些,但是吃得內(nèi)存更多四康,吞吐量更大搪搏。
2:XMX和XMS設(shè)置一樣大,MaxPermSize和MinPermSize設(shè)置一樣大闪金,這樣可以減輕伸縮堆大小帶來的壓力疯溺。
3:調(diào)試的時候設(shè)置一些打印JVM參數(shù),如-XX:+PrintClassHistogram-XX:+PrintGCDetails-XX:+PrintGCTimeStamps-XX:+PrintHeapAtGC-Xloggc:log/gc.log哎垦,這樣可以從gc.log里看出一些端倪出來囱嫩。
4:系統(tǒng)停頓的時候可能是GC的問題也可能是程序的問題,多用jmap和jstack查看漏设,或者killall-3java墨闲,然后查看java控制臺日志,能看出很多問題郑口。有一次鸳碧,網(wǎng)站突然很慢,jstack一看犬性,原來是自己寫的URLConnection連接太多沒有釋放瞻离,改一下程序就OK了。
5:仔細了解自己的應(yīng)用乒裆,如果用了緩存套利,那么年老代應(yīng)該大一些,緩存的HashMap不應(yīng)該無限制長鹤耍,建議采用LRU算法的Map做緩存肉迫,LRUMap的最大長度也要根據(jù)實際情況設(shè)定。
6:垃圾回收時promotionfailed是個很頭痛的問題惰蜜,一般可能是兩種原因產(chǎn)生昂拂,第一個原因是救助空間不夠,救助空間里的對象還不應(yīng)該被移動到年老代抛猖,但年輕代又有很多對象需要放入救助空間;第二個原因是年老代沒有足夠的空間接納來自年輕代的對象;這兩種情況都會轉(zhuǎn)向FullGC格侯,網(wǎng)站停頓時間較長。第一個原因我的最終解決辦法是去掉救助空間财著,設(shè)置-XX:SurvivorRatio=65536-XX:MaxTenuringThreshold=0即可联四,第二個原因我的解決辦法是設(shè)置CMSInitiatingOccupancyFraction為某個值(假設(shè)70),這樣年老代空間到70%時就開始執(zhí)行CMS撑教,年老代有足夠的空間接納來自年輕代的對象朝墩。
7:不管怎樣,永久代還是會逐漸變滿伟姐,所以隔三差五重起java服務(wù)器是必要的收苏,我每天都自動重起亿卤。
8:采用并發(fā)回收時,年輕代小一點鹿霸,年老代要大排吴,因為年老大用的是并發(fā)回收,即使時間長點也不會影響其他程序繼續(xù)運行懦鼠,網(wǎng)站不會停頓钻哩。
gc算法
垃圾收集算法
標記 -清除算法
“標記-清除”(Mark-Sweep)算法,如它的名字一樣肛冶,算法分為“標記”和“清除”兩個階段:首先標記出所有需要回收的對象街氢,在標記完成后統(tǒng)一回收掉所有被標記的對象。之所以說它是最基礎(chǔ)的收集算法睦袖,是因為后續(xù)的收集算法都是基于這種思路并對其缺點進行改進而得到的珊肃。
它的主要缺點有兩個:一個是效率問題,標記和清除過程的效率都不高;另外一個是空間問題扣泊,標記清除之后會產(chǎn)生大量不連續(xù)的內(nèi)存碎片近范,空間碎片太多可能會導致,當程序在以后的運行過程中需要分配較大對象時無法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾收集動作延蟹。
復制算法
“復制”(Copying)的收集算法评矩,它將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊阱飘。當這一塊的內(nèi)存用完了斥杜,就將還存活著的對象復制到另外一塊上面,然后再把已使用過的內(nèi)存空間一次清理掉沥匈。
這樣使得每次都是對其中的一塊進行內(nèi)存回收蔗喂,內(nèi)存分配時也就不用考慮內(nèi)存碎片等復雜情況,只要移動堆頂指針高帖,按順序分配內(nèi)存即可缰儿,實現(xiàn)簡單,運行高效散址。只是這種算法的代價是將內(nèi)存縮小為原來的一半设褐,持續(xù)復制長生存期的對象則導致效率降低萧福。
標記-壓縮算法
復制收集算法在對象存活率較高時就要執(zhí)行較多的復制操作,效率將會變低。更關(guān)鍵的是西轩,如果不想浪費50%的空間铣缠,就需要有額外的空間進行分配擔保揭糕,以應(yīng)對被使用的內(nèi)存中所有對象都100%存活的極端情況柱蟀,所以在老年代一般不能直接選用這種算法。
根據(jù)老年代的特點,有人提出了另外一種“標記-整理”(Mark-Compact)算法蹈矮,標記過程仍然與“標記-清除”算法一樣砰逻,但后續(xù)步驟不是直接對可回收對象進行清理,而是讓所有存活的對象都向一端移動含滴,然后直接清理掉端邊界以外的內(nèi)存
小編推薦一個學JAVA的學習裙【 一三三诱渤,九三零,六九三】谈况,無論你是大牛還是小白,是想轉(zhuǎn)行還是想入行都可以來了解一起進步一起學習递胧!裙內(nèi)有開發(fā)工具碑韵,很多貨和技術(shù)資料分享
分代收集算法
GC分代的基本假設(shè):絕大部分對象的生命周期都非常短暫,存活時間短缎脾。
“分代收集”(Generational Collection)算法祝闻,把Java堆分為新生代和老年代,這樣就可以根據(jù)各個年代的特點采用最適當?shù)氖占惴ㄒ挪ぁT谛律辛看卫占瘯r都發(fā)現(xiàn)有大批對象死去,只有少量存活辙纬,那就選用復制算法豁遭,只需要付出少量存活對象的復制成本就可以完成收集。而老年代中因為對象存活率高贺拣、沒有額外空間對它進行分配擔保蓖谢,就必須使用“標記-清理”或“標記-整理”算法來進行回收。
垃圾收集器
如果說收集算法是內(nèi)存回收的方法論譬涡,垃圾收集器就是內(nèi)存回收的具體實現(xiàn)
Serial收集器
串行收集器是最古老闪幽,最穩(wěn)定以及效率高的收集器,可能會產(chǎn)生較長的停頓涡匀,只使用一個線程去回收盯腌。新生代、老年代使用串行回收;新生代復制算法陨瘩、老年代標記-壓縮;垃圾收集的過程中會Stop The World(服務(wù)暫停)
參數(shù)控制:-XX:+UseSerialGC 串行收集器
spring中bean的作用域
Spring 3中為Bean定義了5中作用域腕够,分別為singleton(單例)、prototype(原型)拾酝、request燕少、session和global session,5種作用域說明如下:
singleton:單例模式蒿囤,Spring IoC容器中只會存在一個共享的Bean實例客们,無論有多少個Bean引用它,始終指向同一對象。Singleton作用域是Spring中的缺省作用域底挫,也可以顯示的將Bean定義為singleton模式恒傻,配置為:
prototype:原型模式,每次通過Spring容器獲取prototype定義的bean時建邓,容器都將創(chuàng)建一個新的Bean實例盈厘,每個Bean實例都有自己的屬性和狀態(tài),而singleton全局只有一個對象官边。根據(jù)經(jīng)驗沸手,對有狀態(tài)的bean使用prototype作用域,而對無狀態(tài)的bean使用singleton作用域注簿。
request:在一次Http請求中契吉,容器會返回該Bean的同一實例。而對不同的Http請求則會產(chǎn)生新的Bean诡渴,而且該bean僅在當前Http Request內(nèi)有效捐晶。
,針對每一次Http請求,Spring容器根據(jù)該bean的定義創(chuàng)建一個全新的實例妄辩,且該實例僅在當前Http請求內(nèi)有效惑灵,而其它請求無法看到當前請求中狀態(tài)的變化,當當前Http請求結(jié)束眼耀,該bean實例也將會被銷毀英支。
session:在一次Http Session中,容器會返回該Bean的同一實例畔塔。而對不同的Session請求則會創(chuàng)建新的實例潭辈,該bean實例僅在當前Session內(nèi)有效。
,同Http請求相同澈吨,每一次session請求創(chuàng)建新的實例把敢,而不同的實例之間不共享屬性,且實例僅在自己的session請求內(nèi)有效谅辣,請求結(jié)束修赞,則實例將被銷毀。
global Session:在一個全局的Http Session中桑阶,容器會返回該Bean的同一個實例柏副,僅在使用portlet context時有效。
spring中單例的bean是不是線程安全的
Spring框架并沒有對單例bean進行任何多線程的封裝處理蚣录。關(guān)于單例bean的線程安全和并發(fā)問題需要開發(fā)者自行去搞定割择。但實際上,大部分的Spring bean并沒有可變的狀態(tài)(比如Serview類和DAO類)萎河,所以在某種程度上說Spring的單例bean是線程安全的荔泳。如果你的bean有多種狀態(tài)的話(比如 View Model 對象)蕉饼,就需要自行保證線程安全。
最淺顯的解決辦法就是將多態(tài)bean的作用域由“singleton”變更為“prototype”玛歌。
java8中stream有幾種
Java中的Stream的所有操作都是針對流的昧港,所以,使用Stream必須要得到Stream對象:
1支子、初始化一個流:
Stream stream = Stream.of("a", "b", "c");
2创肥、數(shù)組轉(zhuǎn)換為一個流:
String [] strArray = new String[] {"a", "b", "c"};
stream = Stream.of(strArray);
或者
stream = Arrays.stream(strArray);
3、集合對象轉(zhuǎn)換為一個流(Collections):
List list = Arrays.asList(strArray);
stream = list.stream();
spring springboot springcloud 的關(guān)系與區(qū)別
Spring Boot框架的核心就是自動配置值朋,只要存在相應(yīng)的jar包叹侄,Spring就幫我們自動配置
1、Spring boot 是 Spring 的一套快速配置腳手架昨登,可以基于spring boot 快速開發(fā)單個微服務(wù);Spring Cloud是一個基于Spring Boot實現(xiàn)的云應(yīng)用開發(fā)工具;
2圈膏、Spring boot專注于快速、方便集成的單個個體篙骡,Spring Cloud是關(guān)注全局的服務(wù)治理框架;
3、spring boot使用了默認大于配置的理念丈甸,很多集成方案已經(jīng)幫你選擇好了糯俗,能不配置就不配置,Spring Cloud很大的一部分是基于Spring boot來實現(xiàn)睦擂。
4得湘、Spring boot可以離開Spring Cloud獨立使用開發(fā)項目,但是Spring Cloud離不開Spring boot顿仇,屬于依賴的關(guān)系淘正。
過濾器和攔截器的區(qū)別
攔截器,在AOP(Aspect-Oriented Programming)中用于在某個方法或字段被訪問之前臼闻,進行攔截然后在之前或之后加入某些操作鸿吆。攔截是AOP的一種實現(xiàn)策略。
過濾器是一個程序述呐,它先于與之相關(guān)的servlet或JSP頁面運行在服務(wù)器上惩淳。過濾器可附加到一個或多個servlet或JSP頁面上,并且可以檢查進入這些資源的請求信息乓搬。
zookeeper的數(shù)據(jù)結(jié)構(gòu)
ZooKeeper這種數(shù)據(jù)結(jié)構(gòu)有如下這些特點:
1. 每個子目錄項如NameService都被稱作znode,這個znode是被它所在的路徑唯一標識,如Server1這個znode的標識為/NameService/Server1
2. znode可以有子節(jié)點目錄,并且每個znode可以存儲數(shù)據(jù),注意EPHEMERAL類型的目錄節(jié)點不能有子節(jié)點目錄
3. znode是有版本的,每個znode中存儲的數(shù)據(jù)可以有多個版本,也就是一個訪問路徑中可以存儲多份數(shù)據(jù)
4. znode可以是臨時節(jié)點,一旦創(chuàng)建這個znode的客戶端與服務(wù)器失去聯(lián)系,這個znode也將自動刪除,ZooKeeper的客戶端和服務(wù)器通信采用長連接方式,每個客戶端和服務(wù)器通過心跳來保持連接,這個連接狀態(tài)成為session,如果znode是臨時節(jié)點,這個session失效,znode也就被刪除.
5. znode的目錄名可以自動編號,如App1已經(jīng)存在,再創(chuàng)建的話,將會自動命名為App2.
6. znode可以被監(jiān)控,包括這個目錄節(jié)點中存儲的數(shù)據(jù)被修改,子節(jié)點目錄的變化等,一旦變化可以通知設(shè)置監(jiān)控的客戶端,這個是ZooKeeper的核心特性.
java8中實現(xiàn)for循環(huán)continue的方式是
在使用foreach()處理集合時不能使用break和continue這兩個方法思犁,也就是說不能按照普通的for循環(huán)遍歷集合時那樣根據(jù)條件來中止遍歷,而如果要實現(xiàn)在普通for循環(huán)中的效果時进肯,可以使用return來達到激蹲,也就是說如果你在一個方法的lambda表達式中使用return時,這個方法是不會返回的江掩,而只是執(zhí)行下一次遍歷
索引的數(shù)據(jù)結(jié)構(gòu)
(1)B-Tree
(2)B+Tree
(3)檢索過程:首先在最上層節(jié)點進行二分查找学辱,如果找不到則去對應(yīng)左子節(jié)點或右子節(jié)點進行二分查找乘瓤,以此遞歸
(4)B-Tree和B+Tree區(qū)別
—內(nèi)節(jié)點不存儲data,只存儲key
—葉子節(jié)點不存儲指針(葉節(jié)點和內(nèi)節(jié)點大小一般不相同)
—葉子節(jié)點具有指向相鄰葉子節(jié)點的指針(方便區(qū)間訪問)
(5)索引設(shè)計思路
—索引本身也很大项郊,不能直接在主存中存儲馅扣,而是存在磁盤上。一個好的索引數(shù)據(jù)結(jié)構(gòu)應(yīng)該最大程度減少磁盤IO着降。
—數(shù)據(jù)庫設(shè)計者巧妙地將【樹節(jié)點】用【單位頁】進行對應(yīng)存儲差油,這樣一個節(jié)點的載入只需要一次磁盤IO。在B-Tree定義中任洞,檢索一次最多需要訪問h(樹高度)個節(jié)點蓄喇,相應(yīng)地最多需要h-1次IO(根節(jié)點常駐內(nèi)存),時間復雜度O(h)= O(logdN)交掏,h為樹高度妆偏,d為單個節(jié)點中Key數(shù)量,d越大盅弛,索引性能越好钱骂。一般d比較大,即橫向比較長挪鹏,h比較小见秽,通常不超過3,因此B-Tree作為索引結(jié)構(gòu)效率非常高讨盒。
(6)為什么不用二叉樹或紅黑樹作為索引數(shù)據(jù)結(jié)構(gòu)?
h較深解取,邏輯上相鄰的父子節(jié)點在物理上可能很遠,無法利用局部性
(7)為什么B+Tree優(yōu)于B-Tree?
因為d越大返顺,索引性能越好禀苦,dmax=floor(pagesize/(keysize+datasize+pointsize)),由于B+Tree內(nèi)節(jié)點去掉了data域遂鹊,因此可以擁有更大的出度振乏,擁有更好的性能
Mysql索引有哪些
從數(shù)據(jù)結(jié)構(gòu)角度
1、B+樹索引(O(log(n))):關(guān)于B+樹索引稿辙,可以參考 MySQL索引背后的數(shù)據(jù)結(jié)構(gòu)及算法原理
2昆码、hash索引:
a 僅僅能滿足"=","IN"和"<=>"查詢,不能使用范圍查詢
b 其檢索效率非常高邻储,索引的檢索可以一次定位赋咽,不像B-Tree 索引需要從根節(jié)點到枝節(jié)點,最后才能訪問到頁節(jié)點這樣多次的IO訪問吨娜,所以 Hash 索引的查詢效率要遠高于 B-Tree 索引
c 只有Memory存儲引擎顯示支持hash索引
3脓匿、FULLTEXT索引(現(xiàn)在MyISAM和InnoDB引擎都支持了)
4、R-Tree索引(用于對GIS數(shù)據(jù)類型創(chuàng)建SPATIAL索引)
從物理存儲角度
1宦赠、聚集索引(clustered index)
2陪毡、非聚集索引(non-clustered index)
從邏輯角度
1米母、主鍵索引:主鍵索引是一種特殊的唯一索引,不允許有空值
2毡琉、普通索引或者單列索引
3铁瞒、多列索引(復合索引):復合索引指多個字段上創(chuàng)建的索引,只有在查詢條件中使用了創(chuàng)建索引時的第一個字段桅滋,索引才會被使用慧耍。使用復合索引時遵循最左前綴集合
4、唯一索引或者非唯一索引
5丐谋、空間索引:空間索引是對空間數(shù)據(jù)類型的字段建立的索引芍碧,MYSQL中的空間數(shù)據(jù)類型有4種,分別是GEOMETRY号俐、POINT泌豆、LINESTRING、POLYGON吏饿。MYSQL使用SPATIAL關(guān)鍵字進行擴展踪危,使得能夠用于創(chuàng)建正規(guī)索引類型的語法創(chuàng)建空間索引。創(chuàng)建空間索引的列猪落,必須將其聲明為NOT NULL陨倡,空間索引只能在存儲引擎為MYISAM的表中創(chuàng)建
CREATE TABLE table_name[col_name data type]
[unique|fulltext|spatial][index|key][index_name](col_name[length])[asc|desc]
1、unique|fulltext|spatial為可選參數(shù)许布,分別表示唯一索引、全文索引和空間索引;
2绎晃、index和key為同義詞蜜唾,兩者作用相同,用來指定創(chuàng)建索引
3庶艾、col_name為需要創(chuàng)建索引的字段列袁余,該列必須從數(shù)據(jù)表中該定義的多個列中選擇;
4、index_name指定索引的名稱咱揍,為可選參數(shù)颖榜,如果不指定,MYSQL默認col_name為索引值;
5煤裙、length為可選參數(shù)掩完,表示索引的長度,只有字符串類型的字段才能指定索引長度;
6硼砰、asc或desc指定升序或降序的索引值存儲
ORM
大概地說且蓬,這類框架的是為了將類對象和關(guān)系建立映射,在應(yīng)用程序和數(shù)據(jù)庫的IO之間建立一個中間層题翰,在程序中只需要直接操作對象(數(shù)據(jù)庫中對象的增刪改查)恶阴,而不用去關(guān)心數(shù)據(jù)庫中表的列啊诈胜,關(guān)系啊什么的
相關(guān)技術(shù):JPA 、hibernate等 沒必要自己寫冯事,自己寫出來的也不一定有他們的好焦匈,并且一些判斷、提示都沒有他們的完善
事務(wù)的傳播行為和隔離級別[transaction behavior and isolated level]
Spring中事務(wù)的定義:
一昵仅、Propagation :
key屬性確定代理應(yīng)該給哪個方法增加事務(wù)行為缓熟。這樣的屬性最重要的部份是傳播行為。有以下選項可供使用:
PROPAGATION_REQUIRED--支持當前事務(wù)岩饼,如果當前沒有事務(wù)荚虚,就新建一個事務(wù)。這是最常見的選擇籍茧。
PROPAGATION_SUPPORTS--支持當前事務(wù)版述,如果當前沒有事務(wù),就以非事務(wù)方式執(zhí)行寞冯。
PROPAGATION_MANDATORY--支持當前事務(wù)渴析,如果當前沒有事務(wù),就拋出異常吮龄。
PROPAGATION_REQUIRES_NEW--新建事務(wù)俭茧,如果當前存在事務(wù),把當前事務(wù)掛起漓帚。
PROPAGATION_NOT_SUPPORTED--以非事務(wù)方式執(zhí)行操作母债,如果當前存在事務(wù),就把當前事務(wù)掛起尝抖。
PROPAGATION_NEVER--以非事務(wù)方式執(zhí)行毡们,如果當前存在事務(wù),則拋出異常昧辽。
很多人看到事務(wù)的傳播行為屬性都不甚了解衙熔,我昨晚看了j2ee without ejb的時候,看到這里也不了解搅荞,甚至重新翻起數(shù)據(jù)庫系統(tǒng)的教材書红氯,但是也沒有找到對這個的分析。今天搜索咕痛,找到一篇極好的分析文章痢甘,雖然這篇文章是重點分析PROPAGATION_REQUIRED 和 PROPAGATION_REQUIRED_NESTED的
解惑 spring 嵌套事務(wù)
這里一個個分析
1: PROPAGATION_REQUIRED
加入當前正要執(zhí)行的事務(wù)不在另外一個事務(wù)里,那么就起一個新的事務(wù)
比如說茉贡,ServiceB.methodB的事務(wù)級別定義為PROPAGATION_REQUIRED, 那么由于執(zhí)行ServiceA.methodA的時候产阱,
ServiceA.methodA已經(jīng)起了事務(wù),這時調(diào)用ServiceB.methodB块仆,ServiceB.methodB看到自己已經(jīng)運行在ServiceA.methodA
的事務(wù)內(nèi)部构蹬,就不再起新的事務(wù)王暗。而假如ServiceA.methodA運行的時候發(fā)現(xiàn)自己沒有在事務(wù)中,他就會為自己分配一個事務(wù)庄敛。
這樣壹置,在ServiceA.methodA或者在ServiceB.methodB內(nèi)的任何地方出現(xiàn)異常兄墅,事務(wù)都會被回滾酿秸。即使ServiceB.methodB的事務(wù)已經(jīng)被
提交爽雄,但是ServiceA.methodA在接下來fail要回滾,ServiceB.methodB也要回滾
2: PROPAGATION_SUPPORTS
如果當前在事務(wù)中怖亭,即以事務(wù)的形式運行涎显,如果當前不再一個事務(wù)中,那么就以非事務(wù)的形式運行
這就跟平常用的普通非事務(wù)的代碼只有一點點區(qū)別了兴猩。不理這個期吓,因為我也沒有覺得有什么區(qū)別
3: PROPAGATION_MANDATORY
必須在一個事務(wù)中運行。也就是說倾芝,他只能被一個父事務(wù)調(diào)用讨勤。否則,他就要拋出異常晨另。
4: PROPAGATION_REQUIRES_NEW
這個就比較繞口了潭千。 比如我們設(shè)計ServiceA.methodA的事務(wù)級別為PROPAGATION_REQUIRED,ServiceB.methodB的事務(wù)級別為PROPAGATION_REQUIRES_NEW借尿,
那么當執(zhí)行到ServiceB.methodB的時候刨晴,ServiceA.methodA所在的事務(wù)就會掛起,ServiceB.methodB會起一個新的事務(wù)路翻,等待ServiceB.methodB的事務(wù)完成以后割捅,
他才繼續(xù)執(zhí)行。他與PROPAGATION_REQUIRED 的事務(wù)區(qū)別在于事務(wù)的回滾程度了帚桩。因為ServiceB.methodB是新起一個事務(wù),那么就是存在
兩個不同的事務(wù)嘹黔。如果ServiceB.methodB已經(jīng)提交账嚎,那么ServiceA.methodA失敗回滾,ServiceB.methodB是不會回滾的儡蔓。如果ServiceB.methodB失敗回滾郭蕉,
如果他拋出的異常被ServiceA.methodA捕獲,ServiceA.methodA事務(wù)仍然可能提交喂江。
5: PROPAGATION_NOT_SUPPORTED
當前不支持事務(wù)召锈。比如ServiceA.methodA的事務(wù)級別是PROPAGATION_REQUIRED ,而ServiceB.methodB的事務(wù)級別是PROPAGATION_NOT_SUPPORTED 获询,
那么當執(zhí)行到ServiceB.methodB時涨岁,ServiceA.methodA的事務(wù)掛起拐袜,而他以非事務(wù)的狀態(tài)運行完,再繼續(xù)ServiceA.methodA的事務(wù)梢薪。
6: PROPAGATION_NEVER
不能在事務(wù)中運行蹬铺。假設(shè)ServiceA.methodA的事務(wù)級別是PROPAGATION_REQUIRED, 而ServiceB.methodB的事務(wù)級別是PROPAGATION_NEVER 秉撇,
那么ServiceB.methodB就要拋出異常了甜攀。
7: PROPAGATION_NESTED
理解Nested的關(guān)鍵是savepoint。他與PROPAGATION_REQUIRES_NEW的區(qū)別是琐馆,PROPAGATION_REQUIRES_NEW另起一個事務(wù)规阀,將會與他的父事務(wù)相互獨立,
而Nested的事務(wù)和他的父事務(wù)是相依的瘦麸,他的提交是要等和他的父事務(wù)一塊提交的谁撼。也就是說,如果父事務(wù)最后回滾瞎暑,他也要回滾的彤敛。
二、Isolation Level(事務(wù)隔離等級):
1了赌、Serializable:最嚴格的級別墨榄,事務(wù)串行執(zhí)行,資源消耗最大;
2勿她、REPEATABLE READ:保證了一個事務(wù)不會修改已經(jīng)由另一個事務(wù)讀取但未提交(回滾)的數(shù)據(jù)袄秩。避免了“臟讀取”和“不可重復讀取”的情況,但是帶來了更多的性能損失逢并。
3之剧、READ COMMITTED:大多數(shù)主流數(shù)據(jù)庫的默認事務(wù)等級,保證了一個事務(wù)不會讀到另一個并行事務(wù)已修改但未提交的數(shù)據(jù)砍聊,避免了“臟讀取”背稼。該級別適用于大多數(shù)系統(tǒng)。
4玻蝌、Read Uncommitted:保證了讀取過程中不會讀取到非法數(shù)據(jù)蟹肘。
隔離級別在于處理多事務(wù)的并發(fā)問題。
我們知道并行可以提高數(shù)據(jù)庫的吞吐量和效率俯树,但是并不是所有的并發(fā)事務(wù)都可以并發(fā)運行帘腹,這需要查看數(shù)據(jù)庫教材的可串行化條件判斷了。
這里就不闡述许饿。
我們首先說并發(fā)中可能發(fā)生的3中不討人喜歡的事情
1: Dirty reads--讀臟數(shù)據(jù)阳欲。也就是說,比如事務(wù)A的未提交(還依然緩存)的數(shù)據(jù)被事務(wù)B讀走,如果事務(wù)A失敗回滾球化,會導致事務(wù)B所讀取的的數(shù)據(jù)是錯誤的秽晚。
2: non-repeatable reads--數(shù)據(jù)不可重復讀。比如事務(wù)A中兩處讀取數(shù)據(jù)-total-的值赊窥。在第一讀的時候爆惧,total是100,然后事務(wù)B就把total的數(shù)據(jù)改成200锨能,事務(wù)A再讀一次扯再,結(jié)果就發(fā)現(xiàn),total竟然就變成200了址遇,造成事務(wù)A數(shù)據(jù)混亂熄阻。
3: phantom reads--幻象讀數(shù)據(jù),這個和non-repeatable reads相似倔约,也是同一個事務(wù)中多次讀不一致的問題秃殉。但是non-repeatable reads的不一致是因為他所要取的數(shù)據(jù)集被改變了(比如total的數(shù)據(jù)),但是phantom reads所要讀的數(shù)據(jù)的不一致卻不是他所要讀的數(shù)據(jù)集改變浸剩,而是他的條件數(shù)據(jù)集改變钾军。比如Select account.id where account.name="ppgogo*",第一次讀去了6個符合條件的id,第二次讀取的時候绢要,由于事務(wù)b把一個帳號的名字由"dd"改成"ppgogo1"吏恭,結(jié)果取出來了7個數(shù)據(jù)。
Dirty reads non-repeatable reads phantom reads
Serializable 不會 不會 不會
REPEATABLE READ 不會 不會 會
READ COMMITTED 不會 會 會
Read Uncommitted 會 會 會
三重罪、readOnly
事務(wù)屬性中的readOnly標志表示對應(yīng)的事務(wù)應(yīng)該被最優(yōu)化為只讀事務(wù)樱哼。這是一個最優(yōu)化提示。在一些情況下剿配,一些事務(wù)策略能夠起到顯著的最優(yōu)化效果搅幅,例如在使用Object/Relational映射工具(如:Hibernate或TopLink)時避免dirty checking(試圖“刷新”)。
四呼胚、Timeout
在事務(wù)屬性中還有定義“timeout”值的選項茄唐,指定事務(wù)超時為幾秒。在JTA中蝇更,這將被簡單地傳遞到J2EE服務(wù)器的事務(wù)協(xié)調(diào)程序沪编,并據(jù)此得到相應(yīng)的解釋。
web安全
前后端約定好加密方式簿寂,通過修改request 的parameter
繼承HttpServletRequestWrapper,其實就是上面那種方法多了一層繼承宿亡,將你的重復工作交予了它常遂,你也可以這樣做,
全名為:javax.servlet.http.HttpServletRequestWrapper挽荠,看來也是一個擴展的通用接口克胳,也就是會對request做一次包裝平绩,OK;跟著進去發(fā)現(xiàn)它可以處理類似request一樣的差不多的內(nèi)容,在這個基礎(chǔ)上做了一次包裝漠另,你可以認為他就是對你自己new的那個捏雌,多了一層簡單擴展實現(xiàn),而你再這個基礎(chǔ)上笆搓,可以繼續(xù)繼承和重寫性湿。
OK,此時你要重寫如何重寫呢满败,比如我們要重寫一個getParameter方法和getParameterValues方法肤频,其余的方法保持和原來一致,我們在子類中算墨,自己定義一個Map用來放參數(shù)宵荒,結(jié)合request本身的參數(shù),加上外部其他自定義的參數(shù)净嘀,做成一個新的參數(shù)表报咳。
我們用到的是反射機制 雖然網(wǎng)上有說我們是在裸奔,但是還沒來得及改挖藏。暑刃。
java并發(fā)包
1、ConcurrentHashMap
ConcurrentHashMap其實就是線程安全版本的hashMap熬苍。前面我們知道HashMap是以鏈表的形式存放hash沖突的數(shù)據(jù)稍走,以數(shù)組形式存放HashEntry等hash出來不一致的數(shù)據(jù)。為了保證容器的數(shù)據(jù)一致性柴底,需要加鎖婿脸。HashMap的實現(xiàn)方式是,只有put和remove的時候會引發(fā)數(shù)據(jù)的不一致柄驻,那為了保證數(shù)據(jù)的一致性狐树,我在put和remove的時候進行加鎖操作。但是隨之而來的是性能問題鸿脓,因為key-value形式的數(shù)據(jù)抑钟,讀寫頻繁是很正常的,也就意味著我有大量數(shù)據(jù)做讀寫操作時會引發(fā)長時間的等待野哭。為了解決這個問題在塔,Java并發(fā)包問我們提供了新的思路。在每一個HashEntry上加一把鎖拨黔,對于hash沖突的數(shù)據(jù)蛔溃,因為采用鏈表存儲,公用一把鎖。這樣我才在做不同hash數(shù)值的數(shù)據(jù)時贺待,則是在不同的鎖環(huán)境下執(zhí)行徽曲,基本上是互不干擾的。在最好情況下麸塞,可以保證16個線程同時進行無阻塞的操作(HashMap的默認HashEntry是16秃臣,亦即默認的數(shù)組大小是16)。
那ConcurrentHashMap是如何保證數(shù)據(jù)操作的一致性呢?對于數(shù)據(jù)元素的大小哪工,ConcurrentHashMap將對應(yīng)數(shù)組(HashEntry的長度)的變量為voliate類型的奥此,也就是任何HashEntry發(fā)生變更,所有的地方都會知道數(shù)據(jù)的大小正勒。對于元素得院,如何保證我取出的元素的next不發(fā)生變更呢?(HashEntry中的數(shù)據(jù)采用鏈表存儲,當讀取數(shù)據(jù)的時候可能又發(fā)生了變更)章贞,這一點祥绞,ConcurrentHashMap采取了最簡單的做法,hash值鸭限、key和next取出后都為final類型的蜕径,其next等數(shù)據(jù)永遠不會發(fā)生變更。
2败京、CopyOnWriteArrayList
同樣的兜喻,CopyOnWriteArrayList是線程安全版本的ArrayList。和ArrayList不同的是赡麦,CopyOnWriteArrayList默認是創(chuàng)建了一個大小為0的容器朴皆。通過ReentrantLock來保證線程安全。CopyOnWriteArrayList其實每次增加的時候泛粹,需要新創(chuàng)建一個比原來容量+1大小的數(shù)組遂铡,然后拷貝原來的元素到新的數(shù)組中,同時將新插入的元素放在最末端晶姊。然后切換引用扒接。
針對CopyOnWriteArrayList,因為每次做插入和刪除操作们衙,都需要重新開辟空間和復制數(shù)組元素钾怔,因此對于插入和刪除元素,CopyOnWriteArrayList的性能遠遠不如ArrayList蒙挑,但是每次讀取的時候宗侦,CopyOnWriteArrayList在不加鎖的情況下直接鎖定數(shù)據(jù),會快很多(但是可能會引發(fā)臟讀)忆蚀,對于迭代矾利,CopyOnWriteArrayList會生成一個快照數(shù)組懊悯,因此當?shù)^程中出現(xiàn)變化,快照數(shù)據(jù)沒有變更梦皮,因此讀到的數(shù)據(jù)也是不會變化的。在讀多寫少的環(huán)境下桃焕,CopyOnWriteArrayList的性能還是不錯的剑肯。
3、CopyOnWriteArraySet
CopyOnWriteArraySet是基于CopyOnWriteArrayList實現(xiàn)的观堂。但是CopyOnWriteArraySet鑒于不能插入重復數(shù)據(jù)让网,因此每次add的時候都要遍歷數(shù)據(jù),性能略低于CopyOnWriteArrayList师痕。
4溃睹、ArrayBlockingQueue
ArrayBlockingQueue是基于數(shù)組實現(xiàn)的一個線程安全的隊列服務(wù),其相關(guān)的功能前面我們已經(jīng)用到過了胰坟,這里就不多提了因篇。
5、Atomic類笔横,如AtomicInteger竞滓、AtomicBoolean
線程問題
1. 進程和線程之間有什么不同?
一個進程是一個獨立(self contained)的運行環(huán)境,它可以被看作一個程序或者一個應(yīng)用吹缔。
而線程是在進程中執(zhí)行的一個任務(wù)商佑。Java運行環(huán)境是一個包含了不同的類和程序的單 一進程。
線程可以被稱為輕量級進程厢塘。線程需要較少的資源來創(chuàng)建和駐留在進程中茶没,并且可以共享進程中的資源。
2. 多線程編程的好處是什么?
在多線程程序中晚碾,多個線程被并發(fā)的執(zhí)行以提高程序的效率抓半,CPU不會因為某個線程需要等待資源而進入空閑狀態(tài)。
多個線程共享堆內(nèi)存(heap memory)迄薄,因此創(chuàng)建多個線程去執(zhí)行一些任務(wù)會比創(chuàng)建多個進程更好琅关。
舉個例子,Servlets比CGI更好讥蔽,是因為Servlets支持多線程而 CGI不支持涣易。
3. 用戶線程和守護線程有什么區(qū)別?
當我們在Java程序中創(chuàng)建一個線程,它就被稱為用戶線程冶伞。
一個守護線程是在后臺執(zhí)行并且不會阻止JVM終止的線程新症。
當沒有用戶線程在運行的時候,JVM關(guān)閉程序并且退出响禽。
一個守護線程創(chuàng)建的子線程依然是守護線程徒爹。
4. 我們?nèi)绾蝿?chuàng)建一個線程?
有兩種創(chuàng)建線程的方法:
一是實現(xiàn)Runnable接口荚醒,然后將它傳遞給Thread的構(gòu)造函數(shù),創(chuàng)建一個Thread對象;
二是直接繼承Thread類隆嗅。
5. 有哪些不同的線程生命周期?
當我們在Java程序中新建一個線程時界阁,它的狀態(tài)是New。當我們調(diào)用線程的start()方法時胖喳,狀態(tài)被改變?yōu)镽unnable泡躯。
線程調(diào)度器會為Runnable線程池中的線程分配CPU時間并且講它們的狀態(tài)改變?yōu)镽unning。
其他的線程狀態(tài)還有Waiting丽焊,Blocked 和Dead较剃。
6. 可以直接調(diào)用Thread類的run()方法么?
當然可以,但是如果我們調(diào)用了Thread的run()方法技健,它的行為就會和普通的方法一樣写穴,為了在新的線程中執(zhí)行我們的代碼,必須使用Thread.start()方法雌贱。
7. 如何讓正在運行的線程暫停一段時間?
我們可以使用Thread類的Sleep()方法讓線程暫停一段時間啊送。需要注意的是,這并不會讓線程終止欣孤,一旦從休眠中喚醒線程删掀,線程的狀態(tài)將會被改變?yōu)镽unnable,并且根據(jù)線程調(diào)度导街,它將得到執(zhí)行披泪。
8. 你對線程優(yōu)先級的理解是什么?
每一個線程都是有優(yōu)先級的,一般來說搬瑰,高優(yōu)先級的線程在運行時會具有優(yōu)先權(quán)款票,
但這依賴于線程調(diào)度的實現(xiàn),這個實現(xiàn)是和操作系統(tǒng)相關(guān)的(OS dependent)泽论。
我們可以定義線程的優(yōu)先級艾少,但是這并不能保證高優(yōu)先級的線程會在低優(yōu)先級的線程前執(zhí)行。
線程優(yōu)先級是一個int變量(從 1-10)翼悴,1代表最低優(yōu)先級缚够,10代表最高優(yōu)先級。
9. 什么是線程調(diào)度器(Thread Scheduler)和時間分片(Time Slicing)?
線程調(diào)度器是一個操作系統(tǒng)服務(wù)鹦赎,它負責為Runnable狀態(tài)的線程分配CPU時間谍椅。
一旦我們創(chuàng)建一個線程并啟動它,它的執(zhí)行便依賴于線程調(diào)度器的實現(xiàn)古话。
時間分片是指將可用的CPU時間分配給可用的Runnable線程的過程雏吭。
分配CPU時間可以基于線程優(yōu)先級或者線程等待的時間维蒙。
線程調(diào)度并不受到Java虛擬機控制层玲,所以由應(yīng)用程序來控制它是更好的選擇(也就是說不要讓你的程序依賴于線程的優(yōu)先級)痒筒。
10. 在多線程中袜腥,什么是上下文切換(context-switching)?
上下文切換是存儲和恢復CPU狀態(tài)的過程,它使得線程執(zhí)行能夠從中斷點恢復執(zhí)行摘完。
上下文切換是多任務(wù)操作系統(tǒng)和多線程環(huán)境的基本特征姥饰。
11. 你如何確保main()方法所在的線程是Java程序最后結(jié)束的線程?
我們可以使用Thread類的joint()方法來確保所有程序創(chuàng)建的線程在main()方法退出前結(jié)束。
12.線程之間是如何通信的?
當線程間是可以共享資源時孝治,線程間通信是協(xié)調(diào)它們的重要的手段媳否。
Object類中wait() otify() otifyAll()方法可以用于線程間通信關(guān)于資源的鎖的狀態(tài)。
13.為什么線程通信的方法wait(), notify()和notifyAll()被定義在Object類里?
Java的每個對象中都有一個鎖(monitor荆秦,也可以成為監(jiān)視器) 并且wait(),
notify()等方法用于等待對象的鎖或者通知其他線程對象的監(jiān)視器可用力图。
在Java的線程中并沒有可供任何對象使用的鎖和同步器步绸。
這就是為什么這些方法是Object類的一部分,這樣Java的每一個類都有用于線程間通信的基本方法
14. 為什么wait(), notify()和notifyAll()必須在同步方法或者同步塊中被調(diào)用?
當一個線程需要調(diào)用對象的wait()方法的時候吃媒,這個線程必須擁有該對象的鎖瓤介,
接著它就會釋放這個對象鎖并進入等待狀態(tài)直到其他線程調(diào)用這個對象 上的notify()方法。
同樣的赘那,當一個線程需要調(diào)用對象的notify()方法時刑桑,它會釋放這個對象的鎖,以便其他在等待的線程就可以得到這個對象 鎖募舟。
由于所有的這些方法都需要線程持有對象的鎖祠斧,這樣就只能通過同步來實現(xiàn),所以他們只能在同步方法或者同步塊中被調(diào)用拱礁。
15. 為什么Thread類的sleep()和yield()方法是靜態(tài)的?
Thread類的sleep()和yield()方法將在當前正在執(zhí)行的線程上運行琢锋。‘
所以在其他處于等待狀態(tài)的線程上調(diào)用這些方法是沒有意義的呢灶。
這 就是為什么這些方法是靜態(tài)的吴超。它們可以在當前正在執(zhí)行的線程中工作,并避免程序員錯誤的認為可以在其他非運行線程調(diào)用這些方法鸯乃。
16.如何確保線程安全?
在Java中可以有很多方法來保證線程安全——同步鲸阻,使用原子類(atomic concurrent classes),實現(xiàn)并發(fā)鎖缨睡,使用volatile關(guān)鍵字鸟悴,使用不變類和線程安全類。
17. volatile關(guān)鍵字在Java中有什么作用?
當我們使用volatile關(guān)鍵字去修飾變量的時候奖年,所以線程都會直接讀取該變量并且不緩存它遣臼。
這就確保了線程讀取到的變量是同內(nèi)存中是一致的。
18. 同步方法和同步塊拾并,哪個是更好的選擇?
同步塊是更好的選擇揍堰,因為它不會鎖住整個對象(當然你也可以讓它鎖住整個對象)鹏浅。
同步方法會鎖住整個對象,哪怕這個類中有多個不相關(guān)聯(lián)的同步塊屏歹,這通常會導致他們停止執(zhí)行并需要等待獲得這個對象上的鎖隐砸。
19.如何創(chuàng)建守護線程?
使用Thread類的setDaemon(true)方法可以將線程設(shè)置為守護線程,
需要注意的是蝙眶,需要在調(diào)用start()方法前調(diào)用這個方法季希,否則會拋出IllegalThreadStateException異常。
20. 什么是ThreadLocal
ThreadLocal用于創(chuàng)建線程的本地變量幽纷,我們知道一個對象的所有線程會共享它的全局變量式塌,
所以這些變量不是線程安全的,我們可以使用同步技術(shù)友浸。但是當我們不想使用同步的時候峰尝,我們可以選擇ThreadLocal變量。
每個線程都會擁有他們自己的Thread變量收恢,它們可以使用get()set()方法去獲取他們的默認值或者在線程內(nèi)部改變他們的值武学。
小編推薦一個學JAVA的學習裙【 一三三,九三零伦意,六九三】火窒,無論你是大牛還是小白,是想轉(zhuǎn)行還是想入行都可以來了解一起進步一起學習驮肉!裙內(nèi)有開發(fā)工具熏矿,很多貨和技術(shù)資料分享ThreadLocal實例通常是希望它們同線程狀態(tài)關(guān)聯(lián)起來是private static屬性。
21. 什么是Thread Group?為什么建議使用它?
ThreadGroup是一個類离钝,它的目的是提供關(guān)于線程組的信息曲掰。
ThreadGroup API比較薄弱,它并沒有比Thread提供了更多的功能奈辰。
它有兩個主要的功能:一是獲取線程組中處于活躍狀態(tài)線程的列表;
二是設(shè)置為線程設(shè)置未捕獲異常 處理器(ncaught exception handler)栏妖。
但在Java 1.5中Thread類也添加了setUncaughtExceptionHandler(UncaughtExceptionHandler eh) 方法,
所以ThreadGroup是已經(jīng)過時的奖恰,不建議繼續(xù)使用吊趾。
22. 什么是Java線程轉(zhuǎn)儲(Thread Dump),如何得到它?
線程轉(zhuǎn)儲是一個JVM活動線程的列表瑟啃,它對于分析系統(tǒng)瓶頸和死鎖非常有用论泛。
有很多方法可以獲取線程轉(zhuǎn)儲——使用Profiler,Kill -3命令蛹屿,jstack工具等等屁奏。我更喜歡jstack工具,因為它容易使用并且是JDK自帶的错负。由于它是一個基于終端的工具坟瓢,所以我們可以編寫一些腳本 去定時的產(chǎn)生線程轉(zhuǎn)儲以待分析勇边。讀這篇文檔可以了解更多關(guān)于產(chǎn)生線程轉(zhuǎn)儲的知識。
23. 什么是死鎖(Deadlock)?如何分析和避免死鎖?
死鎖是指兩個以上的線程永遠阻塞的情況折联,這種情況產(chǎn)生至少需要兩個以上的線程和兩個以上的資源粒褒。
分析死鎖,我們需要查看Java應(yīng)用程序的線程轉(zhuǎn)儲诚镰。我們需要找出那些狀態(tài)為BLOCKED的線程和他們等待的資源奕坟。每個資源都有一個唯一的id,用這個id我們可以找出哪些線程已經(jīng)擁有了它的對象鎖清笨。
避免嵌套鎖月杉,只在需要的地方使用鎖和避免無限期等待是避免死鎖的通常辦法,閱讀這篇文章去學習如何分析死鎖抠艾。
24. 什么是Java Timer類?如何創(chuàng)建一個有特定時間間隔的任務(wù)?
java.util.Timer是一個工具類苛萎,可以用于安排一個線程在未來的某個特定時間執(zhí)行。Timer類可以用安排一次性任務(wù)或者周期任務(wù)跌帐。
java.util.TimerTask是一個實現(xiàn)了Runnable接口的抽象類,我們需要去繼承這個類來創(chuàng)建我們自己的定時任務(wù)并使用Timer去安排它的執(zhí)行绊率。
這里有關(guān)于java Timer的例子谨敛。
25. 什么是線程池?如何創(chuàng)建一個Java線程池?
一個線程池管理了一組工作線程,同時它還包括了一個用于放置等待執(zhí)行的任務(wù)的隊列滤否。
java.util.concurrent.Executors提供了一個 java.util.concurrent.Executor接口的實現(xiàn)用于創(chuàng)建線程池脸狸。線程池例子展現(xiàn)了如何創(chuàng)建和使用線程池,或者閱讀ScheduledThreadPoolExecutor例子藐俺,了解如何創(chuàng)建一個周期任務(wù)炊甲。
Java并發(fā)面試問題
1. 什么是原子操作?在Java Concurrency API中有哪些原子類(atomic classes)?
原子操作是指一個不受其他操作影響的操作任務(wù)單元。原子操作是在多線程環(huán)境下避免數(shù)據(jù)不一致必須的手段欲芹。
int++并不是一個原子操作卿啡,所以當一個線程讀取它的值并加1時,另外一個線程有可能會讀到之前的值菱父,這就會引發(fā)錯誤颈娜。
為了解決這個問題,必須保證增加操作是原子的浙宜,在JDK1.5之前我們可以使用同步技術(shù)來做到這一點官辽。到 JDK1.5,java.util.concurrent.atomic包提供了int和long類型的裝類粟瞬,它們可以自動的保證對于他們的操作是原子的 并且不需要使用同步同仆。可以閱讀這篇文章來了解Java的atomic類裙品。
2. Java Concurrency API中的Lock接口(Lock interface)是什么?對比同步它有什么優(yōu)勢?
Lock接口比同步方法和同步塊提供了更具擴展性的鎖操作俗批。他們允許更靈活的結(jié)構(gòu)俗或,可以具有完全不同的性質(zhì),并且可以支持多個相關(guān)類的條件對象扶镀。
它的優(yōu)勢有:
可以使鎖更公平
可以使線程在等待鎖的時候響應(yīng)中斷
可以讓線程嘗試獲取鎖蕴侣,并在無法獲取鎖的時候立即返回或者等待一段時間
可以在不同的范圍,以不同的順序獲取和釋放鎖
閱讀更多關(guān)于鎖的例子
3. 什么是Executors框架?
Executor框架同java.util.concurrent.Executor 接口在Java 5中被引入臭觉。Executor框架是一個根據(jù)一組執(zhí)行策略調(diào)用昆雀,調(diào)度,執(zhí)行和控制的異步任務(wù)的框架蝠筑。
無限制的創(chuàng)建線程會引起應(yīng)用程序內(nèi)存溢出狞膘。所以創(chuàng)建一個線程池是個更好的的解決方案,因為可以限制線程的數(shù)量并且可以回收再利用這些線程什乙。利用Executors框架可以非常方便的創(chuàng)建一個線程池挽封,閱讀這篇文章可以了解如何使用Executor框架創(chuàng)建一個線程池。
4. 什么是阻塞隊列?如何使用阻塞隊列來實現(xiàn)生產(chǎn)者-消費者模型?
java.util.concurrent.BlockingQueue的特性是:當隊列是空的時臣镣,從隊列中獲取或刪除元素的操作將會被阻塞辅愿,或者當隊列是滿時,往隊列里添加元素的操作會被阻塞忆某。
阻塞隊列不接受空值点待,當你嘗試向隊列中添加空值的時候,它會拋出NullPointerException弃舒。
阻塞隊列的實現(xiàn)都是線程安全的癞埠,所有的查詢方法都是原子的并且使用了內(nèi)部鎖或者其他形式的并發(fā)控制。
BlockingQueue 接口是java collections框架的一部分聋呢,它主要用于實現(xiàn)生產(chǎn)者-消費者問題苗踪。
閱讀這篇文章了解如何使用阻塞隊列實現(xiàn)生產(chǎn)者-消費者問題。
5. 什么是Callable和Future
Java 5在concurrency包中引入了java.util.concurrent.Callable 接口削锰,它和Runnable接口很相似通铲,但它可以返回一個對象或者拋出一個異常。
Callable接口使用泛型去定義它的返回類型器贩。Executors類提供了一些有用的方法去在線程池中執(zhí)行Callable內(nèi)的任務(wù)测暗。由于 Callable任務(wù)是并行的,我們必須等待它返回的結(jié)果磨澡。java.util.concurrent.Future對象為我們解決了這個問題碗啄。在線程池 提交Callable任務(wù)后返回了一個Future對象,使用它我們可以知道Callable任務(wù)的狀態(tài)和得到Callable返回的執(zhí)行結(jié)果稳摄。 Future提供了get()方法讓我們可以等待Callable結(jié)束并獲取它的執(zhí)行結(jié)果稚字。
閱讀這篇文章了解更多關(guān)于Callable,F(xiàn)uture的例子。
6. 什么是FutureTask
FutureTask是Future的一個基礎(chǔ)實現(xiàn)胆描,我們可以將它同Executors使用處理異步任務(wù)瘫想。通常我們不需要使用FutureTask 類,單當我們打算重寫Future接口的一些方法并保持原來基礎(chǔ)的實現(xiàn)是昌讲,它就變得非常有用国夜。我們可以僅僅繼承于它并重寫我們需要的方法。閱讀Java FutureTask例子短绸,學習如何使用它车吹。
7.什么是并發(fā)容器的實現(xiàn)?
Java集合類都是快速失敗的,這就意味著當集合被改變且一個線程在使用迭代器遍歷集合的時候醋闭,迭代器的next()方法將拋出ConcurrentModificationException異常窄驹。
并發(fā)容器支持并發(fā)的遍歷和并發(fā)的更新。
主要的類有ConcurrentHashMap, CopyOnWriteArrayList 和CopyOnWriteArraySet证逻,閱讀這篇文章了解如何避免ConcurrentModificationException乐埠。
8. Executors類是什么?
Executors為Executor,ExecutorService囚企,ScheduledExecutorService丈咐,ThreadFactory和Callable類提供了一些工具方法。
Executors可以用于方便的創(chuàng)建線程池龙宏。
冒泡排序的實現(xiàn)原理
冒泡排序(Bubble Sort)棵逊,是一種計算機科學領(lǐng)域的較簡單的排序算法。
它重復地走訪過要排序的數(shù)列烦衣,一次比較兩個元素歹河,如果他們的順序錯誤就把他們交換過來掩浙。走訪數(shù)列的工作是重復地進行直到?jīng)]有再需要交換花吟,也就是說該數(shù)列已經(jīng)排序完成。
這個算法的名字由來是因為越大的元素會經(jīng)由交換慢慢“浮”到數(shù)列的頂端厨姚,故名衅澈。
冒泡排序算法的原理如下:(從后往前)
比較相鄰的元素。如果第一個比第二個大谬墙,就交換他們兩個今布。
對每一對相鄰元素作同樣的工作,從開始第一對到結(jié)尾的最后一對拭抬。在這一點部默,最后的元素應(yīng)該會是最大的數(shù)。
針對所有的元素重復以上的步驟造虎,除了最后一個傅蹂。
持續(xù)每次對越來越少的元素重復上面的步驟,直到?jīng)]有任何一對數(shù)字需要比較。
二叉樹遍歷的三種方法
1份蝴、遞歸法
這是思路最簡單的方法犁功,容易想到并且容易實現(xiàn)。遞歸的終止條件是當前節(jié)點是否為空婚夫。首先遞歸調(diào)用遍歷左子樹浸卦,然后訪問當前節(jié)點,最后遞歸調(diào)用右子樹案糙。代碼如下:
[cpp] view plain copy
//recursive
class Solution1 {
public:
vector inorderTraversal(TreeNode* root) {
vector ret;
if(root==NULL)return ret;
inorderHelper(ret,root);
return ret;
}
private:
void inorderHelper(vector& ret,TreeNode* root)
{
if(root==NULL)return;
inorderHelper(ret,root->left);
ret.push_back(root->val);
inorderHelper(ret,root->right);
}
};
2限嫌、迭代法
在迭代方法中,從根節(jié)點開始找二叉樹的最左節(jié)點侍筛,將走過的節(jié)點保存在一個棧中萤皂,找到最左節(jié)點后訪問,對于每個節(jié)點來說匣椰,它都是以自己為根的子樹的根節(jié)點裆熙,訪問完之后就可以轉(zhuǎn)到右兒子上了。代碼如下:
[cpp] view plain copy
//iterate,using a stack
class Solution2 {
public:
vector inorderTraversal(TreeNode* root) {
vector ret;
if(root==NULL)return ret;
TreeNode *curr=root;
stack st;
while(!st.empty()||curr!=NULL)
{
while(curr!=NULL)
{
st.push(curr);
curr=curr->left;
}
curr=st.top();
st.pop();
ret.push_back(curr->val);
curr=curr->right;
}
return ret;
}
};
這種方法時間復雜度是O(n)禽笑,空間復雜度也是O(n)入录。
3、Morris法
這種方法是Morris發(fā)明的佳镜,看完之后感覺精妙無比僚稿。這種方法不使用遞歸,不使用棧蟀伸,O(1)的空間復雜度完成二叉樹的遍歷蚀同。這種方法的基本思路就是將所有右兒子為NULL的節(jié)點的右兒子指向后繼節(jié)點(對于右兒子不為空的節(jié)點,右兒子就是接下來要訪問的節(jié)點)啊掏。這樣蠢络,對于任意一個節(jié)點,當訪問完它后迟蜜,它的右兒子已經(jīng)指向了下一個該訪問的節(jié)點刹孔。對于最右節(jié)點,不需要進行這樣的操作娜睛。注意髓霞,這樣的操作是在遍歷的時候完成的,完成訪問節(jié)點后會把樹還原畦戒。整個循環(huán)的判斷條件為當前節(jié)點是否為空方库。例如上面的二叉樹,遍歷過程如下(根據(jù)當前節(jié)點c的位置):
(1)當前節(jié)點為10障斋,因為左兒子非空纵潦,不能訪問,找到c的左子樹的最右節(jié)點p:
小編推薦一個學JAVA的學習裙【 一三三,九三零酪穿,六九三】凳干,無論你是大牛還是小白,是想轉(zhuǎn)行還是想入行都可以來了解一起進步一起學習被济!裙內(nèi)有開發(fā)工具救赐,很多貨和技術(shù)資料分享
結(jié)果:[]
(2)找節(jié)點c的左子樹的最右節(jié)點有兩種終止條件,一種右兒子為空只磷,一種右兒子指向當前節(jié)點经磅。下面是右兒子為空的情況,這種情況先要構(gòu)造钮追,將節(jié)點p的右兒子指向后繼節(jié)點c预厌,然后c下移:
結(jié)果:[]
(3)當前節(jié)點c的左兒子為空,進行訪問元媚。訪問后將c指向右兒子(即后繼節(jié)點):
結(jié)果:[5]
(4)繼續(xù)尋找左子樹的最右節(jié)點轧叽,這次的終止條件是最右節(jié)點為當前節(jié)點。這說明當前節(jié)點的左子樹遍歷完畢刊棕,訪問當前節(jié)點后炭晒,還原二叉樹,將當前節(jié)點指向后繼節(jié)點:
結(jié)果:[5,10]
(5)重復上述過程甥角,直到c指向整棵二叉樹的最右節(jié)點:
左兒子為空网严,進行訪問,c轉(zhuǎn)到右兒子嗤无。右兒子為空震束,不滿足判斷條件,循環(huán)結(jié)束当犯,遍歷完成垢村。結(jié)果如下:
[5,10,6,15,2]
這就是Morris方法,時間復雜度為O(n)灶壶,空間復雜度是O(1)肝断。代碼如下:
[cpp] view plain copy
//Morris traversal,without a stack
class Solution3 {
public:
vector inorderTraversal(TreeNode* root) {
vector ret;
if(root==NULL)return ret;
TreeNode *curr=root;
TreeNode *pre;
while(curr)
{
if(curr->left==NULL)
{
ret.push_back(curr->val);
curr=curr->right;
}
else
{
pre=curr->left;
while(pre->right&&pre->right!=curr)
pre=pre->right;
if(pre->right==NULL)
{
pre->right=curr;
curr=curr->left;
}
else
{
ret.push_back(curr->val);
pre->right=NULL;
curr=curr->right;
}
}
}
return ret;
}
};
TCP三次握手
第一次握手:建立連接時杈曲,客戶端發(fā)送syn包(syn=j)到服務(wù)器驰凛,并進入SYN_SENT狀態(tài),等待服務(wù)器確認;SYN:同步序列編號(Synchronize Sequence Numbers)担扑。
第二次
第二次握手:服務(wù)器收到syn包恰响,必須確認客戶的SYN(ack=j+1),同時自己也發(fā)送一個SYN包(syn=k)涌献,即SYN+ACK包胚宦,此時服務(wù)器進入SYN_RECV狀態(tài);
第三次
第三次握手:客戶端收到服務(wù)器的SYN+ACK包,向服務(wù)器發(fā)送確認包ACK(ack=k+1),此包發(fā)送完畢枢劝,客戶端和服務(wù)器進入ESTABLISHED(TCP連接成功)狀態(tài)井联,完成三次握手。
java數(shù)據(jù)結(jié)構(gòu)
數(shù)據(jù)結(jié)構(gòu)分類:線性結(jié)構(gòu)和非線性結(jié)構(gòu)
問題一:
什么是線性和非線性;
我個人的理解是:數(shù)據(jù)結(jié)構(gòu)中線性結(jié)構(gòu)指的是數(shù)據(jù)元素之間存在著“一對一”的線性關(guān)系的數(shù)據(jù)結(jié)構(gòu);
線性結(jié)構(gòu)包括:數(shù)組您旁,鏈表烙常,隊列,棧;
非線性結(jié)構(gòu)包括:樹鹤盒,圖蚕脏,表;
詳解:
一.線性結(jié)構(gòu)
1.數(shù)組
特點:我們都知道數(shù)組中的元素在內(nèi)存中連續(xù)存儲的,可以根據(jù)是下標快速訪問元素侦锯,因此驼鞭,查詢速度很快,然而插入和刪除時尺碰,需要對元素移動空間挣棕,比較慢。
數(shù)組使用場景:頻繁查詢亲桥,很少增加和刪除的情況穴张。
2.鏈表
特點:元素可以不連續(xù)內(nèi)存中,是以索引將數(shù)據(jù)聯(lián)系起來的两曼,當查詢元素的時候需要從頭開始查詢皂甘,所以效率比較低,然而添加和刪除的只需要修改索引就可以了
使用場景:少查詢悼凑,需要頻繁的插入或刪除情況
3.隊列
特點:先進先出偿枕,
使用場景:多線程阻塞隊列管理非常有用
4.棧
特點:先進后出,就像一個箱子户辫,
使用場景:實現(xiàn)遞歸以及表示式
5.數(shù)組與鏈表的區(qū)別
數(shù)組連續(xù)渐夸,鏈表不連續(xù)(從數(shù)據(jù)存儲形式來說)
數(shù)組內(nèi)存靜態(tài)分配,鏈表動態(tài)分配
數(shù)組查詢復雜度0(1)渔欢,鏈表查詢復雜度O(n)
數(shù)組添加或刪除墓塌,復雜度o(n),鏈表添加刪除,復雜度O(1)
數(shù)組從棧中分配內(nèi)存奥额。鏈表從堆中分配內(nèi)存苫幢。
Java內(nèi)存溢出問題的定位過程
對于java.lang.OutOfMemoryError: PermGen space 這種情況更多的是靠程序猿的經(jīng)驗來解決:
PermGen space的全稱是Permanent Generation space,是指內(nèi)存的永久保存區(qū)域, 這塊內(nèi)存主要是被JVM存放Class和Meta信息的,Class在被Load時就會被放到PermGen space中, 它和存放類實例(Instance)的Heap區(qū)域不同,GC(Garbage Collection)不會在主程序運行期對 PermGen space進行清理,所以如果你的應(yīng)用中有很多CLASS的話,就很可能出現(xiàn)PermGen space錯誤垫挨。
通過上面的描述就可以得出:如果要加載的class與jar文件大小超過-XX:MaxPermSize就有可能會產(chǎn)生java.lang.OutOfMemoryError: PermGen space 状蜗。
換句話說-XX:MaxPermSize的大小要超過class與jar的大小疾棵。通常-XX:MaxPermSize為-Xmx的1/8。
對于java.lang.OutOfMemoryError: Java heap space 可能定位的過程就需要折騰一翻了:
雖然各種java虛擬機的實現(xiàn)機制不一,但是heap space內(nèi)存溢出產(chǎn)生的原因相同:那就是堆內(nèi)存不夠虛擬機分配了鸯绿。
我對java虛擬機的實現(xiàn)不感興趣,對各種虛擬機的內(nèi)存分配機制與gc的交互關(guān)系也不了解。但是我大致認為內(nèi)存分配機制與gc是有聯(lián)系的,也就是說內(nèi)存不夠分配時gc肯定也釋放不了堆內(nèi)存。從這一點出發(fā)催蝗,我們就需要找為什么gc釋放不了堆內(nèi)存。通常來說釋放不了是因為內(nèi)存還在使用育特。對于java對象產(chǎn)生的堆內(nèi)存占用生逸,只要其不再被繼續(xù)引用gc是能夠順利回收的(對于通過本地方法調(diào)用,即JNI調(diào)用產(chǎn)生內(nèi)存泄露的情況暫不考慮)。
問題的關(guān)鍵就找到了且预,當產(chǎn)生heap space內(nèi)存溢出時槽袄,堆內(nèi)存中對象數(shù)量過多的就可能是問題的根源了。例外的情況是锋谐,程序確實需要那么多內(nèi)存遍尺,這時就要考慮增大堆內(nèi)存。
java sleep和wait的區(qū)別
首先涮拗,要記住這個差別乾戏,“sleep是Thread類的方法,wait是Object類中定義的方法”。盡管這兩個方法都會影響線程的執(zhí)行行為三热,但是本質(zhì)上是有區(qū)別的鼓择。
Thread.sleep不會導致鎖行為的改變,如果當前線程是擁有鎖的就漾,那么Thread.sleep不會讓線程釋放鎖呐能。如果能夠幫助你記憶的話,可以簡單認為和鎖相關(guān)的方法都定義在Object類中抑堡,因此調(diào)用Thread.sleep是不會影響鎖的相關(guān)行為摆出。
Thread.sleep和Object.wait都會暫停當前的線程,對于CPU資源來說首妖,不管是哪種方式暫停的線程偎漫,都表示它暫時不再需要CPU的執(zhí)行時間。OS會將執(zhí)行時間分配給其它線程有缆。區(qū)別是象踊,調(diào)用wait后,需要別的線程執(zhí)行notify/notifyAll才能夠重新獲得CPU執(zhí)行時間棚壁。
線程的狀態(tài)參考 Thread.State的定義杯矩。新創(chuàng)建的但是沒有執(zhí)行(還沒有調(diào)用start())的線程處于“就緒”,或者說Thread.State.NEW狀態(tài)灌曙。
Thread.State.BLOCKED(阻塞)表示線程正在獲取鎖時菊碟,因為鎖不能獲取到而被迫暫停執(zhí)行下面的指令节芥,一直等到這個鎖被別的線程釋放在刺。BLOCKED狀態(tài)下線程逆害,OS調(diào)度機制需要決定下一個能夠獲取鎖的線程是哪個,這種情況下蚣驼,就是產(chǎn)生鎖的爭用魄幕,無論如何這都是很耗時的操作。
字符串哈西相等,equals相等嗎反過來呢
在java中颖杏,equals和hashcode是有設(shè)計要求的纯陨,equals相等,則hashcode一定相等留储,反之則不然翼抠。
為何會有這樣的要求?
在集合中,比如HashSet中获讳,要求放入的對象不能重復阴颖,怎么判定呢?
首先會調(diào)用hashcode,如果hashcode相等丐膝,則繼續(xù)調(diào)用equals量愧,也相等,則認為重復帅矗。
如果重寫equals后偎肃,如果不重寫hashcode,則hashcode就是繼承自O(shè)bject的浑此,返回內(nèi)存編碼累颂,這時候可能出現(xiàn)equals相等,而hashcode不等凛俱,你的對象使用集合時喘落,就會等不到正確的結(jié)果
Java中的幾種設(shè)計模式
如果從事JAVA相關(guān)的開發(fā),都不可避免的要用到抽象和封裝最冰,這是JAVA的一個特點瘦棋,同時也是每個開發(fā)者必須掌握的,JAVA是這樣暖哨,Android更是如此赌朋。而設(shè)計模式就是告訴我們應(yīng)該如何寫出高效且更具應(yīng)用性和拓展性的代碼,最近也是學習了幾類比較常用的設(shè)計模式篇裁,下面一一列舉出來沛慢,雖然說的不細,但是應(yīng)該知道的我會做個總結(jié)达布。
#####單例設(shè)計模式#####
單例設(shè)計模式的一般定義:一個類中只允許有一個實例团甲。
實現(xiàn)思路:讓類的構(gòu)造方法私有化,同時提供一個靜態(tài)方法去實例化這個類黍聂。
代碼 :
static class SingleTon {
private static SingleTon instance;
private SingleTon(){};
public static SingleTon newInstance() {
if(instance==null) {
synchronized (SingleTon.class) {
if(instance==null) {
ins和tance=new SingleTon();
}
}
}
return instance;
}
這是一個較為標準的單例模式躺苦,為了安全我給他加了鎖身腻,然而這樣寫并不是最好的寫法。單例模式有兩種寫法匹厘,懶漢寫法和餓漢寫法嘀趟。
懶漢式:在靜態(tài)方法中初始化。時間換空間愈诚。(不推薦她按,時間很重要)
餓漢式:在聲明對象就初始化】蝗幔空間換時間酌泰。(推薦,空間不是問題)
所以匕累,在實際開發(fā)中用的更多的是餓漢寫法宫莱,可以對這個類加鎖,在變量聲明的時候就初始化哩罪。具體如何實現(xiàn)這里我就不介紹了授霸,可以自己去實現(xiàn)。
#####簡單工廠設(shè)計模式#####
簡單工廠設(shè)計模式的一般定義:簡單工廠又叫靜態(tài)工廠际插,由一個工廠對象決定創(chuàng)建哪一個產(chǎn)品對象碘耳。
實現(xiàn)思路:寫一個類,讓他制造出我們想要的對象框弛。
代碼:
public class 學生工廠 {
public static 學生 getStudent(int type) {
switch (type) {
case 學生類型.學神:
return new 學神();
case 學生類型.學霸:
return new 學霸();
case 學生類型.學弱:
return new 學弱();
case 學生類型.學渣:
return new 學渣();
default:
break;
}
return null;
}
}
通過這個工廠辛辨,我們可以很方便的生產(chǎn)出我們想要的對象。這里我用偽代碼表示瑟枫,為了更加形象斗搞,實際中這樣的寫法是絕對不允許的!
#####適配器設(shè)計模式#####
適配器模式的一般定義:某類繼承這個適配器,從而實現(xiàn)我們需要實現(xiàn)的方法慷妙。
實現(xiàn)思路:通過寫一個適配器類僻焚,里面寫了所有的抽象方法,但是這些方法是空的膝擂,并且適配器類要定義成抽象的虑啤,如果適配器類可以自己實現(xiàn)就沒有意義了。適配器的作用架馋,繼承適配器狞山,重新需要重新的方法就可以了,簡化操作叉寂。
優(yōu)點
* 客戶端不需要在負責對象的創(chuàng)建萍启,從而明確了各個類的職責,如果有新的對象增加屏鳍,只需要增加一個具體的類和具體的工廠類即可勘纯,不影響已有的代碼局服,后期維護容易,增強了系統(tǒng)的擴展性屡律。
缺點
* 需要額外的編寫代碼腌逢,增加了工作量
代碼:
public class Adapter {
public static void main(String[] args) {
阿樂 a=new 阿樂();
a.啪啪啪();
}
interface 談戀愛 {
void 吃飯();
void 看電影();
void 逛街();
void 啪啪啪();
}
abstract class 戀愛中的妹子 implements 談戀愛{
@Override
public void 吃飯() {
// TODO Auto-generated method stub
}
@Override
public void 看電影() {
// TODO Auto-generated method stub
}
@Override
public void 逛街() {
// TODO Auto-generated method stub
}
@Override
public void 啪啪啪() {
// TODO Auto-generated method stub
}
}
class 阿樂 extends 戀愛中的妹子{
public void 啪啪啪() {
// TODO Auto-generated method stub
System.out.println("阿樂:亞美爹降淮,好害羞");
}
}
}
這里我寫了一個接口超埋,接口中定義了幾個方法,當某類實現(xiàn)這個接口的時候就需要實現(xiàn)這些方法佳鳖。用時適配器不允許自己實現(xiàn)這些方法霍殴,需要交給他的子類去繼承,讓子類選擇需要實現(xiàn)什么代碼系吩,Android中的Adapter就是用了適配器模式来庭。
#####模板設(shè)計模式#####
模板設(shè)計模式的一般定義:定義一個算法骨架將具體實現(xiàn)交給子類去實現(xiàn)。
實現(xiàn)方法:在類中定義一個抽象方法穿挨,距離實現(xiàn)交由子類去實現(xiàn)
代碼:
public class A {
public final void run {
........
void howRun();
........
}
public abstract void howRun();
}
public class B extends A {
public void howRun () {
..........
}
}
優(yōu)點
* 使用模版方法模式月弛,在定義算法骨架的同時,可以很靈活的實現(xiàn)具體的算法科盛,滿足用戶靈活多變的需求
缺點
* 如果算法骨架有修改的話帽衙,則需要修改抽象類
MySQL InnoDB事務(wù)隔離級別臟讀、可重復讀贞绵、幻讀
MySQL InnoDB事務(wù)的隔離級別有四級厉萝,默認是“可重復讀”(REPEATABLE READ)。
· 1).未提交讀(READUNCOMMITTED)榨崩。另一個事務(wù)修改了數(shù)據(jù)谴垫,但尚未提交,而本事務(wù)中的SELECT會讀到這些未被提交的數(shù)據(jù)(臟讀)( 隔離級別最低母蛛,并發(fā)性能高 )翩剪。
· 2).提交讀(READCOMMITTED)。本事務(wù)讀取到的是最新的數(shù)據(jù)(其他事務(wù)提交后的)彩郊。問題是肢专,在同一個事務(wù)里,前后兩次相同的SELECT會讀到不同的結(jié)果(不重復讀)焦辅。會出現(xiàn)不可重復讀博杖、幻讀問題(鎖定正在讀取的行)
· 3).可重復讀(REPEATABLEREAD)。在同一個事務(wù)里筷登,SELECT的結(jié)果是事務(wù)開始時時間點的狀態(tài)剃根,因此,同樣的SELECT操作讀到的結(jié)果會是一致的前方。但是狈醉,會有幻讀現(xiàn)象(稍后解釋)廉油。會出幻讀(鎖定所讀取的所有行)。
· 4).串行化(SERIALIZABLE)苗傅。讀操作會隱式獲取共享鎖抒线,可以保證不同事務(wù)間的互斥(鎖表)。
‘
四個級別逐漸增強渣慕,每個級別解決一個問題嘶炭。
· 1).臟讀。另一個事務(wù)修改了數(shù)據(jù)逊桦,但尚未提交眨猎,而本事務(wù)中的SELECT會讀到這些未被提交的數(shù)據(jù)。
· 2).不重復讀强经。解決了臟讀后睡陪,會遇到,同一個事務(wù)執(zhí)行過程中匿情,另外一個事務(wù)提交了新數(shù)據(jù)兰迫,因此本事務(wù)先后兩次讀到的數(shù)據(jù)結(jié)果會不一致。
· 3).幻讀炬称。解決了不重復讀汁果,保證了同一個事務(wù)里,查詢的結(jié)果都是事務(wù)開始時的狀態(tài)(一致性)转砖。但是须鼎,如果另一個事務(wù)同時提交了新數(shù)據(jù),本事務(wù)再更新時府蔗,就會“驚奇的”發(fā)現(xiàn)了這些新數(shù)據(jù)晋控,貌似之前讀到的數(shù)據(jù)是“鬼影”一樣的幻覺。
具體地:
1). 臟讀
首先區(qū)分臟頁和臟數(shù)據(jù)
臟頁是內(nèi)存的緩沖池中已經(jīng)修改的page姓赤,未及時flush到硬盤赡译,但已經(jīng)寫到redo log中。讀取和修改緩沖池的page很正常不铆,可以提高效率蝌焚,flush即可同步。臟數(shù)據(jù)是指事務(wù)對緩沖池中的行記錄record進行了修改誓斥,但是還沒提交!!!只洒,如果這時讀取緩沖池中未提交的行數(shù)據(jù)就叫臟讀,違反了事務(wù)的隔離性劳坑。臟讀就是指當一個事務(wù)正在訪問數(shù)據(jù)毕谴,并且對數(shù)據(jù)進行了修改,而這種修改還沒有提交到數(shù)據(jù)庫中,這時涝开,另外一個事務(wù)也訪問這個數(shù)據(jù)循帐,然后使用了這個數(shù)據(jù)。
2). 不可重復讀
是指在一個事務(wù)內(nèi)舀武,多次讀同一數(shù)據(jù)拄养。在這個事務(wù)還沒有結(jié)束時,另外一個事務(wù)也訪問該同一數(shù)據(jù)银舱。那么瘪匿,在第一個事務(wù)中的兩次讀數(shù)據(jù)之間,由于第二個事務(wù)的修改纵朋,第二個事務(wù)已經(jīng)提交柿顶。那么第一個事務(wù)兩次讀到的的數(shù)據(jù)可能是不一樣的茄袖。這樣就發(fā)生了在一個事務(wù)內(nèi)兩次讀到的數(shù)據(jù)是不一樣的操软,因此稱為是不可重復讀。例如宪祥,一個編輯人員兩次讀取同一文檔聂薪,但在兩次讀取之間,作者重寫了該文檔蝗羊。當編輯人員第二次讀取文檔時藏澳,文檔已更改。原始讀取不可重復耀找。如果只有在作者全部完成編寫后編輯人員才可以讀取文檔翔悠,則可以避免該問題
3). 幻讀 :
是指當事務(wù)不是獨立執(zhí)行時發(fā)生的一種現(xiàn)象,例如第一個事務(wù)對一個表中的數(shù)據(jù)進行了修改野芒,這種修改涉及到表中的全部數(shù)據(jù)行蓄愁。同時,第二個事務(wù)也修改這個表中的數(shù)據(jù)狞悲,這種修改是向表中插入一行新數(shù)據(jù)撮抓。那么,以后就會發(fā)生操作第一個事務(wù)的用戶發(fā)現(xiàn)表中還有沒有修改的數(shù)據(jù)行摇锋,就好象發(fā)生了幻覺一樣丹拯。例如,一個編輯人員更改作者提交的文檔荸恕,但當生產(chǎn)部門將其更改內(nèi)容合并到該文檔的主復本時乖酬,發(fā)現(xiàn)作者已將未編輯的新材料添加到該文檔中。如果在編輯人員和生產(chǎn)部門完成對原始文檔的處理之前融求,任何人都不能將新材料添加到文檔中咬像,則可以避免該問題。
結(jié)論:MySQL InnoDB事務(wù)默認隔離級別是可重復讀并不保證避免幻讀,需要應(yīng)用使用加鎖讀來保證施掏。而這個加鎖度使用到的機制就是next-key locks钮惠。
MySQL存儲引擎MyISAM與InnoDB的主要區(qū)別對比
InnoDB與Myisam的六大區(qū)別
MyISAM、InnoDB
構(gòu) 成上的區(qū)別:
每個MyISAM在磁盤上存儲成三個文件七芭。第一個 文件的名字以表的名字開始素挽,擴展名指出文件類型。
.frm文件存儲表定義狸驳。
數(shù)據(jù)文件的擴 展名為.MYD (MYData)预明。
索引文件的擴 展名是.MYI (MYIndex)。
基于磁盤的資源是InnoDB表空間數(shù)據(jù)文件和它的日志文件耙箍,InnoDB 表的 大小只受限于操作系統(tǒng)文件的大小撰糠,一般為 2GB
事務(wù)處理上方面:
MyISAM類型的表強調(diào)的是性能,其執(zhí)行數(shù) 度比InnoDB類型更快辩昆,但是不提供事務(wù)支持
InnoDB提供事務(wù)支持事務(wù)阅酪,外部鍵等高級 數(shù)據(jù)庫功能
SELECT UPDATE,INSERT,Delete操 作
如果執(zhí)行大量的SELECT汁针,MyISAM是更好的選擇
1.如果你的數(shù)據(jù)執(zhí)行大量的INSERT或UPDATE术辐,出于性能方面的考慮,應(yīng)該使用InnoDB表
2.DELETE FROM table時施无,InnoDB不會重新建立表辉词,而是一行一行的 刪除。
3.LOAD TABLE FROM MASTER操作對InnoDB是不起作用的猾骡,解決方法是首先把InnoDB表改成MyISAM表瑞躺,導入數(shù)據(jù)后再改成InnoDB表,但是對于使用的額外的InnoDB特性(例如外鍵)的表不適用
對AUTO_INCREMENT的 操作
每表一個AUTO_INCREMEN列的內(nèi)部處理兴想。
MyISAM為INSERT和UPDATE操 作自動更新這一列幢哨。這使得AUTO_INCREMENT列更快(至少10%)。在序列頂?shù)闹当粍h除之后就不 能再利用襟企。(當AUTO_INCREMENT列被定義為多列索引的最后一列嘱么, 可以出現(xiàn)重使用從序列頂部刪除的值的情況)。
AUTO_INCREMENT值可用ALTER TABLE或myisamch來重置
對于AUTO_INCREMENT類型的字段顽悼,InnoDB中必須包含只有該字段的索引曼振,但 是在MyISAM表中,可以和其他字段一起建立聯(lián) 合索引
更好和更快的auto_increment處理
如果你為一個表指定AUTO_INCREMENT列蔚龙,在數(shù)據(jù)詞典里的InnoDB表句柄包含一個名為自動增長計數(shù) 器的計數(shù)器冰评,它被用在為該列賦新值。
自動增長計數(shù) 器僅被存儲在主內(nèi)存中木羹,而不是存在磁盤上
關(guān)于該計算器 的算法實現(xiàn)甲雅,請參考
AUTO_INCREMENT列 在InnoDB里 如何工作
表 的具體行數(shù)
select count(*) from table,MyISAM只要簡單的讀出保存好的行數(shù)解孙,注意的是,當count(*)語句包含 where條件時抛人,兩種表的操作是一樣的
InnoDB 中不 保存表的具體行數(shù)弛姜,也就是說,執(zhí)行select count(*) from table時妖枚,InnoDB要掃描一遍整個表來計算有多少行
鎖
表鎖
提供行鎖(locking on row level)廷臼,提供與 Oracle 類型一致的不加鎖讀取(non-locking read in
SELECTs),另外绝页,InnoDB表的行鎖也不是絕對的荠商,如果在執(zhí) 行一個SQL語句時MySQL不能確定要掃描的范圍,InnoDB表同樣會鎖全表续誉,例如update table set num=1 where name like “%aaa%”
6種負載均衡算法
1莱没、輪詢法
將請求按順序輪流地分配到后端服務(wù)器上,它均衡地對待后端的每一臺服務(wù)器酷鸦,而不關(guān)心服務(wù)器實際的連接數(shù)和當前的系統(tǒng)負載饰躲。
2、隨機法
通過系統(tǒng)的隨機算法井佑,根據(jù)后端服務(wù)器的列表大小值來隨機選取其中的一臺服務(wù)器進行訪問属铁。由概率統(tǒng)計理論可以得知眠寿,隨著客戶端調(diào)用服務(wù)端的次數(shù)增多躬翁,
其實際效果越來越接近于平均分配調(diào)用量到后端的每一臺服務(wù)器,也就是輪詢的結(jié)果盯拱。
3盒发、源地址哈希法
源地址哈希的思想是根據(jù)獲取客戶端的IP地址,通過哈希函數(shù)計算得到的一個數(shù)值狡逢,用該數(shù)值對服務(wù)器列表的大小進行取模運算宁舰,得到的結(jié)果便是客服端要訪問服務(wù)器的序號。采用源地址哈希法進行負載均衡奢浑,同一IP地址的客戶端蛮艰,當后端服務(wù)器列表不變時,它每次都會映射到同一臺后端服務(wù)器進行訪問雀彼。
4壤蚜、加權(quán)輪詢法
不同的后端服務(wù)器可能機器的配置和當前系統(tǒng)的負載并不相同,因此它們的抗壓能力也不相同徊哑。給配置高袜刷、負載低的機器配置更高的權(quán)重,讓其處理更多的請;而配置低莺丑、負載高的機器著蟹,給其分配較低的權(quán)重墩蔓,降低其系統(tǒng)負載,加權(quán)輪詢能很好地處理這一問題萧豆,并將請求順序且按照權(quán)重分配到后端奸披。
5、加權(quán)隨機法
與加權(quán)輪詢法一樣涮雷,加權(quán)隨機法也根據(jù)后端機器的配置源内,系統(tǒng)的負載分配不同的權(quán)重。不同的是份殿,它是按照權(quán)重隨機請求后端服務(wù)器膜钓,而非順序。
6卿嘲、最小連接數(shù)法
最小連接數(shù)算法比較靈活和智能颂斜,由于后端服務(wù)器的配置不盡相同,對于請求的處理有快有慢拾枣,它是根據(jù)后端服務(wù)器當前的連接情況沃疮,動態(tài)地選取其中當前
積壓連接數(shù)最少的一臺服務(wù)器來處理當前的請求,盡可能地提高后端服務(wù)的利用效率梅肤,將負責合理地分流到每一臺服務(wù)器司蔬。