




版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
java基礎(chǔ)
面向?qū)ο?/p>
什么是面向?qū)ο螅?/p>
對(duì)比面向過(guò)程,是兩種不同的處理問(wèn)題的角度
面向過(guò)程更注重事情的每一個(gè)步驟及順序,面向?qū)ο蟾⒅厥虑橛心男﹨⑴c者(對(duì)象)、及各自需要做
什么
比如:洗衣機(jī)洗衣服
面向過(guò)程會(huì)將任務(wù)拆解成一系列的步驟(函數(shù)),1、打開(kāi)洗衣機(jī)-->2、放衣服>3、放洗衣粉--
>4、清洗…-->5、烘干
面向?qū)ο髸?huì)拆出人和洗衣機(jī)兩個(gè)對(duì)象:
人:打開(kāi)洗衣機(jī)放衣服放洗衣粉
洗衣機(jī):清洗烘干
從以上例子能看出,面向過(guò)程比較直接高效,而面向?qū)ο蟾子趶?fù)用、擴(kuò)展和維護(hù)
面向?qū)ο?/p>
封裝:封裝的意義,在于明確標(biāo)識(shí)出允許外部使用的所有成員函數(shù)和數(shù)據(jù)項(xiàng)
內(nèi)部細(xì)節(jié)對(duì)外部調(diào)用透明,外部調(diào)用無(wú)需修改或者關(guān)心內(nèi)部實(shí)現(xiàn)
1、javabean的屬性私有,提供getset對(duì)外訪問(wèn),因?yàn)閷傩缘馁x值或者獲取邏輯只能由javabean本身決
定。而不能由外部胡亂修改
privateStringname;
publicvoidsetName(Stringname){
this.name="tuling.^+name;
)
該name有自己的命名規(guī)則,明顯不能由外部直接賦值
2、orm框架
操作數(shù)據(jù)庫(kù),我們不需要關(guān)心鏈接是如何建立的、sql是如何執(zhí)行的,只需要引入mybatis,調(diào)方法即可
繼承:繼承基類的方法,并做出自己的改變和/或擴(kuò)展
子類共性的方法或者屬性直接使用父類的,而不需要自己再定義,只需擴(kuò)展自己個(gè)性化的
多態(tài):基于對(duì)象所屬類的不同,外部對(duì)同一個(gè)方法的調(diào)用,實(shí)際執(zhí)行的邏輯不同。
繼承,方法重寫(xiě),父類引用指向子類對(duì)象
父類類型變量名=new子類對(duì)象;
變量名.方法名();
無(wú)法調(diào)用子類特有的功能
JDKJREJVM
JDK:
JavaDevelpmentKitjava開(kāi)發(fā)工具
JRE:
JavaRuntimeEnvironmentjava運(yùn)行時(shí)環(huán)境
JVM:
javaVirtualMachinejava虛擬機(jī)
==fflequalsfctK
==對(duì)比的是棧中的值,基本數(shù)據(jù)類型是變量值,引用類型是堆中內(nèi)存對(duì)象的地址
equals:object中默認(rèn)也是采用=二比較,通常會(huì)重寫(xiě)
Object
publicbooleanequals(Objectobj){
return(this==obj);
)
String
publicbooleanequals(Objectanobject){
if(this==anobject){
returntrue;
)
if(anobject1nstanceofString){
StringanotherString=(String)anObject;
intn=value.length;
if(n==anotherString.value.1ength){
charvl[]=value;
charv2[]=anotherString.value;
inti=0;
while(n--!=0){
if!=v2[i])
returnfalse;
i++;
)
returntrue;
)
)
returnfalse;
)
上述代碼可以看出,String類中被復(fù)寫(xiě)的equals。方法其實(shí)是比較兩個(gè)字符串的內(nèi)容。
publicclassStringDemo{
publicstaticvoidmain(Stringargs[]){
Stringstri="Hello";
Stringstr2=newStringC'Hello");
Stringstr3=str2;//引用傳遞
System.out.println(stri==str2);//false
System.out.println(strl==str3);//false
System.out.println(str2==str3);//true
System.out.println(stri.equals(str2));//true
System.out.println(stri.equals(str3));//true
System.out.println(str2.equals(str3));//true
)
)
hashCode與equals
hashCode介紹:
hashCode。的作用是獲取哈希碼,也稱為散列碼;它實(shí)際上是返回一個(gè)int整數(shù)。這個(gè)哈希碼的作用是
確定該對(duì)象在哈希表中的索引位置。hashCodeO定義在JDK的Object.java中,Java中的任何類都包含有
hashCode()函數(shù)。
散列表存儲(chǔ)的是鍵值對(duì)伙ey-value),它的特點(diǎn)是:能根據(jù)"鍵”快速的檢索出對(duì)應(yīng)的"值"。這其中就利用
到了散列碼?。梢钥焖僬业剿枰膶?duì)象)
為什么要有hashCode:
以“HashSet如何檢查重復(fù)”為例子來(lái)說(shuō)明為什么要有hashCode:
對(duì)象加入HashSet時(shí),HashSet會(huì)先計(jì)算對(duì)象的hashcode值來(lái)判斷對(duì)象加入的位置,看該位置是否有
值,如果沒(méi)有、HashSet會(huì)假設(shè)對(duì)象沒(méi)有重復(fù)出現(xiàn)。但是如果發(fā)現(xiàn)有值,這時(shí)會(huì)調(diào)用equals()方法來(lái)
檢直兩個(gè)對(duì)象是否真的相同。如果兩者相同,HashSet就不會(huì)讓其加入操作成功。如果不同的話,就會(huì)
重新散列到其他位置。這樣就大大減少了equals的次數(shù),相應(yīng)就大大提高了執(zhí)行速度。
?如果兩個(gè)對(duì)象相等,則hashcode一定也是相同的
?兩個(gè)對(duì)象相等,對(duì)兩個(gè)對(duì)象分別調(diào)用equals方法都返回true
?兩個(gè)對(duì)象有相同的hashcode值,它們也不一定是相等的
?因此,equals方法被覆蓋過(guò),則hashCode方法也必須被覆蓋
?hashCode。的默認(rèn)行為是對(duì)堆上的對(duì)象產(chǎn)生獨(dú)特值。如果沒(méi)有重寫(xiě)hashCode。,則該class的兩個(gè)
對(duì)象無(wú)論如何都不會(huì)相等(即使這兩個(gè)對(duì)象指向相同的數(shù)據(jù))
final
最終的
?修飾類:表示類不可被繼承
?修飾方法:表示方法不可被子類覆蓋,但是可以重載
?修飾變量:表示變量一旦被賦值就不可以更改它的值。
(1)修飾成員變量
?如果final修飾的是類變量,只能在靜態(tài)初始化塊中指定初始值或者聲明該類變量時(shí)指定初始值。
?如果fina修飾的是成員變量,可以在非靜態(tài)初始化塊、聲明該變量或者構(gòu)造器中執(zhí)行初始值。
(2)修飾局部變量
系統(tǒng)不會(huì)為局部變量進(jìn)行初始化,局部變量必須由程序員顯示初始化。因此使用final修飾局部變量時(shí),
即可以在定義時(shí)指定默認(rèn)值(后面的代碼不能對(duì)變量再賦值),也可以不指定默認(rèn)值,而在后面的代碼
中對(duì)final變量賦初值(僅一次)
publicclassFinalvar{
finalstaticinta=0;//再聲明的時(shí)候就需要賦值或者靜態(tài)代碼塊賦值
/**
static{
a=0;
}
*/
finalintb=0;//再聲明的大中賦值或者構(gòu)造器賦值
/*{
b=0;
}*/
publicstaticvoidmain(String[]args){
finalintlocalA;〃局部變量只聲明沒(méi)有初始化,不公報(bào)鉛,與final無(wú)關(guān)。
localA=0;//在使用之前一定耍賦值
//localA=1;但是不允許第二次賦值
)
)
(3)修飾基本類型數(shù)據(jù)和引用類型數(shù)據(jù)
?如果是基本數(shù)據(jù)類型的變量,則其數(shù)值一旦在初始化之后便不能更改;
?如果是引用類型的變量,則在對(duì)其初始化之后便不能再讓其指向另一個(gè)對(duì)象。但是引用的值是可變
的。
publicclassFinalReferenceTest{
publicstaticvoidmain(){
finalint[]iArr={l,2,3,4);
■iArr[2]=-3;〃合法
iArr=null;//非法,對(duì)iArr不能重新賦值
finalPersonp=newPerson(25);
p.setAge(24);//合法
p=null;〃非法
)
}
為什么局部?jī)?nèi)部類和匿名內(nèi)部類只能訪問(wèn)局部final變量?
編譯之后會(huì)生成兩個(gè)dass文件,Test.classTesti.class
publicclassTest{
publicstaticvoidmain(String[]args){
)
〃局部final變量a,b
publicvoidtest(finalintb){//jdk8在這里做了優(yōu)化,不用寫(xiě),語(yǔ)法糖,但實(shí)際上也是有
的,也不能修改
finalinta=10;
//匿名內(nèi)部類
newThread(){
publicvoidrun(){
System.out.println(a);
System.out.printin(b);
);
}.start();
}
)
classOutclass{
privateintage=12;
publicvoidoutPrint(finalintx){
classinClass{
publicvoidlnPrint(){
System.out.println(x);
System.out.println(age);
)
}
newinClassO.inPrint();
}
)
首先需要知道的一點(diǎn)是:內(nèi)部類和外部類是處于同一個(gè)級(jí)別的,內(nèi)部類不會(huì)因?yàn)槎x在方法中就會(huì)隨著
方法的執(zhí)行完畢就被銷(xiāo)毀。
這里就會(huì)產(chǎn)生問(wèn)題:當(dāng)外部類的方法結(jié)束時(shí),局部變量就會(huì)被銷(xiāo)毀了,但是內(nèi)部類對(duì)象可能還存在(只有
沒(méi)有人再引用它時(shí),才會(huì)死亡)。這里就出現(xiàn)了一個(gè)矛盾:內(nèi)部類對(duì)象訪問(wèn)了一個(gè)不存在的變量。為了解
決這個(gè)問(wèn)題,就將局部變量復(fù)制了一份作為內(nèi)部類的成員變量,這樣當(dāng)局部變量死亡后,內(nèi)部類仍可以
訪問(wèn)它,實(shí)際訪問(wèn)的是局部變量的“copy"。這樣就好像延長(zhǎng)了局部變量的生命周期
將局部變量復(fù)制為內(nèi)部類的成員變量時(shí),必須保證這兩個(gè)變量是一樣的,也就是如果我們?cè)趦?nèi)部類中修
改了成員變量,方法中的局部變量也得跟著改變,怎么解決問(wèn)題呢?
就將局部變量設(shè)置為final,對(duì)它初始化后,我就不讓你再去修改這個(gè)變量,就保證了內(nèi)部類的成員變量
和方法的局部變量的一致性.這實(shí)際上也是一種妥協(xié)。使得局部變量與內(nèi)部類內(nèi)建立的拷貝保持一致。
String、StringBuffer^StringBuilder
String是final修飾的,不可變,每次操作都會(huì)產(chǎn)生新的String對(duì)象
StringBuffer^nstringBuilder都是在原對(duì)象上操作
StringBuffer是線程安全的,StringBuilder線程不安全的
StringBuffer方法都是synchronized修飾的
性能:StringBuilder>StringBuffer>String
場(chǎng)景:經(jīng)常需要改變字符串內(nèi)容時(shí)使用后面兩個(gè)
優(yōu)先使用StringBuilder,多線程使用共享變量時(shí)使用StringBuffer
重載和重寫(xiě)的區(qū)別
重載:發(fā)生在同一個(gè)類中,方法名必須相同,參數(shù)類型不同、個(gè)數(shù)不同、順序不同,方法返回值和訪問(wèn)
修飾符可以不同,發(fā)生在編譯時(shí)。
重寫(xiě):發(fā)生在父子類中,方法名、參數(shù)列表必須相同,返回值范圍小于等于父類,拋出的異常范圍小于
等于父類,訪問(wèn)修飾符范圍大于等于父類;如果父類方法訪問(wèn)修飾符為private則子類就不能重寫(xiě)該方
法。
publicintadd(inta,Stringb)
publicStringadd(inta,Stringb)
//編譯報(bào)錯(cuò)
接口和抽象類的區(qū)別
?抽象類可以存在普通成員函數(shù),而接口中只能存在publicabstract方法。
?抽象類中的成員變量可以是各種類型的,而接口中的成員變量只能是publicstaticfinal類型的。
?抽象類只能繼承一個(gè),接口可以實(shí)現(xiàn)多個(gè)。
接口的設(shè)計(jì)目的,是對(duì)類的行為進(jìn)行約束(更準(zhǔn)確的說(shuō)是一種“有"約束,因?yàn)榻涌诓荒芤?guī)定類不可以有
什么行為),也就是提供一種機(jī)制,可以強(qiáng)制要求不同的類具有相同的行為。它只約束了行為的有無(wú),
但不對(duì)如何實(shí)現(xiàn)行為進(jìn)行限制。
而抽象類的設(shè)計(jì)目的,是代碼復(fù)用。當(dāng)不同的類具有某些相同的行為(記為行為集合A),且其中一部分行
為的實(shí)現(xiàn)方式一致時(shí)(A的非真子集,記為B),可以讓這些類都派生于一個(gè)抽象類.在這個(gè)抽象類中實(shí)
現(xiàn)了B,避免讓所有的子類來(lái)實(shí)現(xiàn)B,這就達(dá)到了代碼復(fù)用的目的。而A減B的部分,留給各個(gè)子類自己
實(shí)現(xiàn)。正是因?yàn)锳-B在這里沒(méi)有實(shí)現(xiàn),所以抽象類不允許實(shí)例化出來(lái)(否則當(dāng)調(diào)用到A-B時(shí),無(wú)法執(zhí)
行)。
抽象類是對(duì)類本質(zhì)的抽象,表達(dá)的是isa的關(guān)系,比如:BMWisacar.抽象類包含并實(shí)現(xiàn)子類的通
用特性,將子類存在差異化的特性進(jìn)行抽象,交由子類去實(shí)現(xiàn)。
而接口是對(duì)行為的抽象,表達(dá)的是likea的關(guān)系。比如:BirdlikeaAircraft(像飛行器一樣可以
飛),但其本質(zhì)上isaBird,接口的核心是定義行為,即實(shí)現(xiàn)類可以做什么,至于實(shí)現(xiàn)類主體是誰(shuí)、
是如何實(shí)現(xiàn)的,接口并不關(guān)心。
使用場(chǎng)景:當(dāng)你關(guān)注一個(gè)事物的本質(zhì)的時(shí)候,用抽象類;當(dāng)你關(guān)注一個(gè)操作的時(shí)候,用接口.
抽象類的功能要遠(yuǎn)超過(guò)接口,但是,定義抽象類的代價(jià)高。因?yàn)楦呒?jí)語(yǔ)言來(lái)說(shuō)(從實(shí)際設(shè)計(jì)上來(lái)說(shuō)也
是)每個(gè)類只能繼承一個(gè)類。在這個(gè)類中,你必須繼承或編寫(xiě)出其所有子類的所有共性。雖然接口在功
能上會(huì)弱化許多,但是它只是針對(duì)一個(gè)動(dòng)作的描述。而且你可以在一個(gè)類中同時(shí)實(shí)現(xiàn)多個(gè)接口。在設(shè)計(jì)
階段會(huì)降低難度
List和Set的區(qū)別
?List:有序,按對(duì)象進(jìn)入的順序保存對(duì)象,可重復(fù),允許多個(gè)Null元素對(duì)象,可以使用Iterator取出
所有元素,在逐一遍歷,還可以使用get(intindex)獲取指定下標(biāo)的元素
?Set:無(wú)序,不可重復(fù),最多允許有一個(gè)Null元素對(duì)象,取元素時(shí)只能用Iterator接口取得所有元
素,在逐一遍歷各個(gè)元素
ArrayListfOLinkedListlZgi]
ArrayList:基于動(dòng)態(tài)數(shù)組,連續(xù)內(nèi)存存儲(chǔ),適合下標(biāo)訪問(wèn)(隨機(jī)訪問(wèn)),擴(kuò)容機(jī)制:因?yàn)閿?shù)組長(zhǎng)度固
定,超出長(zhǎng)度存數(shù)據(jù)時(shí)需要新建數(shù)組,然后將老數(shù)組的數(shù)據(jù)拷貝到新數(shù)組,如果不是尾部插入數(shù)據(jù)還會(huì)
涉及到元素的移動(dòng)(往后復(fù)制一份,插入新元素),使用尾插法并指定初始容量可以極大提升性能、甚
至超過(guò)linkedList(需要?jiǎng)?chuàng)建大量的node對(duì)象)
LinkedList:基于鏈表,可以存儲(chǔ)在分散的內(nèi)存中,適合做數(shù)據(jù)插入及刪除操作,不適合查詢:需要逐
一遍歷
遍歷LinkedList必須使用iterator不能使用for循環(huán),因?yàn)槊看蝔or循環(huán)體內(nèi)通過(guò)get(i)取得某一元素時(shí)都需
要對(duì)list重新進(jìn)行遍歷,性能消耗極大。
另外不要試圖使用indexOf等返回元素索引,并利用其進(jìn)行遍歷,使用index?僅寸list進(jìn)行了遍歷,當(dāng)結(jié)
果為空時(shí)會(huì)遍歷整個(gè)列表。
HashMap和HashTable有什么區(qū)別?其底層實(shí)現(xiàn)是什
么?
區(qū)別:
(1)HashM叩方法沒(méi)有synchronized修飾,線程非安全,HashTable線程安全;
(2)HashMap允許key和value為null,而HashTable不允許
2.底層實(shí)現(xiàn):數(shù)組+鏈表實(shí)現(xiàn)
jdk8開(kāi)始鏈表高度到8、數(shù)組長(zhǎng)度超過(guò)64,鏈表轉(zhuǎn)變?yōu)榧t黑樹(shù),元素以內(nèi)部類Node節(jié)點(diǎn)存在
?計(jì)算key的hash值,二次hash然后對(duì)數(shù)組長(zhǎng)度取模,對(duì)應(yīng)到數(shù)組下標(biāo),
?如果沒(méi)有產(chǎn)生hash沖突(下標(biāo)位置沒(méi)有元素),則直接創(chuàng)建Node存入數(shù)組,
?如果產(chǎn)生hash沖突,先進(jìn)行equal比較,相同則取代該元素,不同,則判斷鏈表高度插入鏈表,鏈
表高度達(dá)到8,并且數(shù)組長(zhǎng)度到64則轉(zhuǎn)變?yōu)榧t黑樹(shù),長(zhǎng)度低于6則將紅黑樹(shù)轉(zhuǎn)回鏈表
?key為null,存在下標(biāo)0的位置
數(shù)組擴(kuò)容
ConcurrentHashMap原理,上€11<7矛用€11<8版本的區(qū)別
jdk7:
數(shù)據(jù)結(jié)構(gòu):ReentrantLock+Segment+HashEntry,一個(gè)Segment中包含一^T^HashEntry數(shù)組,每個(gè)
HashEntry又是一個(gè)鏈表結(jié)構(gòu)
元素查詢:二次hash,第一次Hash定位?]Segment,第二次Hash定位到元素所在的鏈表的頭部
鎖:Segment分段鎖Segment繼承了ReentrantLock,鎖定操作的Segment,其他的Segment不受影
響,并發(fā)度為segment個(gè)數(shù),可以通過(guò)構(gòu)造函數(shù)指定,數(shù)組擴(kuò)容不會(huì)影響其他的segment
get方法無(wú)需加鎖,vogHie保證
jdk8:
數(shù)據(jù)結(jié)構(gòu):synchronized+CAS+Node+紅黑樹(shù),Node的val和next者B用volatile修飾,保證可見(jiàn)性
查找,替換,賦值操作都使用CAS
鎖:鎖鏈表的head節(jié)點(diǎn),不影響其他元素的讀寫(xiě),鎖粒度更細(xì),效率更高,擴(kuò)容時(shí),阻塞所有的讀寫(xiě)
操作、并發(fā)擴(kuò)容
讀操作無(wú)鎖:
Node的val和next使用volatile修飾,讀寫(xiě)線程對(duì)該變量互相可見(jiàn)
數(shù)組用volatile修飾,保證擴(kuò)容時(shí)被讀線程感知
什么是字節(jié)碼?采用字節(jié)碼的好處是什么?
java中的編譯器和解釋器:
Java中引入了虛擬機(jī)的概念,即在機(jī)器和編譯程序之間加入了一層抽象的虛擬的機(jī)器。這臺(tái)虛擬的機(jī)器
在任何平臺(tái)上都提供給編譯程序一個(gè)的共同的接口。
編譯程序只需要面向虛擬機(jī),生成虛擬機(jī)能夠理解的代碼,然后由解釋器來(lái)將虛擬機(jī)代碼轉(zhuǎn)換為特定系
統(tǒng)的機(jī)器碼執(zhí)行。在Java中,這種供虛擬機(jī)理解的代碼叫做字節(jié)碼(即擴(kuò)展名為.class的文件),它不
面向任何特定的處理器,只面向虛擬機(jī)。
每一種平臺(tái)的解釋器是不同的,但是實(shí)現(xiàn)的虛擬機(jī)是相同的。Java源程序經(jīng)過(guò)編譯器編譯后變成字節(jié)
碼,字節(jié)碼由虛擬機(jī)解釋執(zhí)行,虛擬機(jī)將每一條要執(zhí)行的字節(jié)碼送給解釋器,解釋器將其翻譯成特定機(jī)
器上的機(jī)器碼,然后在特定的機(jī)器上運(yùn)行.這也就是解釋了Java的編譯與解釋并存的特點(diǎn)。
Java源代碼>編譯器—>jvm可執(zhí)彳丁的Java字節(jié)碼(即虛擬指令)>jvm>jvm中解釋器—>機(jī)器可執(zhí)
行的二進(jìn)制機(jī)器碼一,程序運(yùn)行。
采用字節(jié)碼的好處:
Java語(yǔ)言通過(guò)字節(jié)碼的方式,在一定程度上解決了傳統(tǒng)解釋型語(yǔ)言執(zhí)行效率低的問(wèn)題,同時(shí)又保留了解
釋型語(yǔ)言可移植的特點(diǎn)。所以Java程序運(yùn)行時(shí)比較高效,而且,由于字節(jié)碼并不專對(duì)一種特定的機(jī)器,
因此,Java程序無(wú)須重新編譯便可在多種不同的計(jì)算機(jī)上運(yùn)行。
Java中的異常體系
Java中的所有異常都來(lái)自頂級(jí)父類Throwable。
Throwable下有兩個(gè)子類Exception和Error。
Error是程序無(wú)法處理的錯(cuò)誤,一旦出現(xiàn)這個(gè)錯(cuò)誤,則程序?qū)⒈黄韧V惯\(yùn)行。
Exception不會(huì)導(dǎo)致程序停止,又分為兩個(gè)部分RunTimeException運(yùn)行時(shí)異常和CheckedException檢
杳異常。
RunTimeException常常發(fā)生在程序運(yùn)行過(guò)程中,會(huì)導(dǎo)致程序當(dāng)前線程執(zhí)行失敗。CheckedException常
常發(fā)生在程序編譯過(guò)程中,會(huì)導(dǎo)致程序編譯不通過(guò)。
Java類加載器
自帶有三個(gè)類加載器:
JDKbootstrapClassLoader.ExtClassLoader.AppClassLoader0
BootStrapCIassLoader是ExtClassLoader的父類力口載器,默認(rèn)負(fù)責(zé)加載%JAVA_HOME%lib下的jar包和
class文件。
ExtQassLoader是AppCIassLoader的父類力口載器,負(fù)責(zé)加載%狀\6^_1_101\/1£%川13/6乂1:文件夾下的jar包和
class類。
AppCIassLoader是自定義類加載器的父類,負(fù)責(zé)加載classpath下的類文件。系統(tǒng)類加載器,線程上下
文加載器
繼承QassLoader實(shí)現(xiàn)自定義類加載器
雙親委托模型
雙親委派模型的好處:
?主要是為了安全性,避免用戶自己編寫(xiě)的類動(dòng)態(tài)替換Java的一些核心類,比如String。
?同時(shí)也避免了類的重復(fù)加載,因?yàn)镴VM中區(qū)分不同類,不僅僅是根據(jù)類名,相同的class文件被不
同的ClassLoader加載就是不同的兩個(gè)類
GC如何判斷對(duì)象可以被回收
?引用計(jì)數(shù)法:每個(gè)對(duì)象有一個(gè)引用計(jì)數(shù)屬性,新增一個(gè)引用時(shí)計(jì)數(shù)加1,引用釋放時(shí)計(jì)數(shù)減1,計(jì)
數(shù)為0時(shí)可以回收,
?可達(dá)性分析法:從GCRoots開(kāi)始向下搜索,搜索所走過(guò)的路徑稱為引用鏈。當(dāng)一個(gè)對(duì)象到GC
Roots沒(méi)有任何引用鏈相連時(shí),則證明此對(duì)象是不可用的,那么虛擬機(jī)就判斷是可回收對(duì)象。
引用計(jì)數(shù)法,可能會(huì)出現(xiàn)A引用了B,B又引用了A,這時(shí)候就算他們都不再使用了,但因?yàn)橄嗷?/p>
引用計(jì)數(shù)器=1永遠(yuǎn)無(wú)法被回收.
GCRoots的對(duì)象有:
?虛擬機(jī)棧(棧幀中的本地變量表)中引用的對(duì)象
?方法區(qū)中類靜態(tài)屬性引用的對(duì)象
?方法區(qū)中常量引用的對(duì)象
?本地方法棧中JNI(即一般說(shuō)的Native方法)引用的對(duì)象
可達(dá)性算法中的不可達(dá)對(duì)象并不是立即死亡的,對(duì)象擁有一次自我拯救的機(jī)會(huì)。對(duì)象被系統(tǒng)宣告死亡至
少要經(jīng)歷兩次標(biāo)記過(guò)程:第一次是經(jīng)過(guò)可達(dá)性分析發(fā)現(xiàn)沒(méi)有與GCRoots相連接的引用鏈,第二次是在由
虛擬機(jī)自動(dòng)建立的Finalizer隊(duì)列中判斷是否需要執(zhí)行finalize。方法。
當(dāng)對(duì)象變成(GCRoots)不可達(dá)時(shí),GC會(huì)判斷該對(duì)象是否覆蓋了finalize方法,若未覆蓋,則直接將其回
收。否則,若對(duì)象未執(zhí)行過(guò)finalize方法,將其放入F-Queue隊(duì)列,由T氐優(yōu)先級(jí)線程執(zhí)行該隊(duì)列中對(duì)象
的finalize方法。執(zhí)行finalize方法完畢后,GC會(huì)再次判斷該對(duì)象是否可達(dá),若不可達(dá),則進(jìn)行回收,否
則,對(duì)象"復(fù)活"
每個(gè)對(duì)象只能觸發(fā)一次finalize。方法
由于finalize。方法運(yùn)行代價(jià)高昂,不確定性大,無(wú)法保證各個(gè)對(duì)象的調(diào)用順序,不推薦大家使用,建議
遺忘它。
線程、并發(fā)相關(guān)
線程的生命周期?線程有幾種狀態(tài)
1.線程通常有五種狀態(tài),創(chuàng)建,就緒,運(yùn)行、阻塞和死亡狀態(tài)。
2.阻塞的情況又分為三種:
(1)、等待阻塞:運(yùn)行的線程執(zhí)行wait方法,該線程會(huì)釋放占用的所有資源,JVM會(huì)把該線程放入"等待
池”中.進(jìn)入這個(gè)狀態(tài)后,是不能自動(dòng)喚醒的,必須依靠其他線程調(diào)用notify或notifyAII方法才能被喚
醒,wait是object類的方法
(2)、同步阻塞:運(yùn)行的線程在獲取對(duì)象的同步鎖時(shí),若該同步鎖被別的線程占用,則JVM會(huì)把該線程放
入“鎖池”中。
⑶、其他阻塞:運(yùn)行的線程執(zhí)行sleep或join方法,或者發(fā)出了I/O請(qǐng)求時(shí),JVM會(huì)把該線程g為阻塞狀
態(tài)。當(dāng)sleep狀態(tài)超時(shí)、join等待線程終止或者超時(shí)、或者I/。處理完畢時(shí),線程重新轉(zhuǎn)入就緒狀態(tài).
sleep是Thread類的方法
1新建狀態(tài)(New):新創(chuàng)建了f線程對(duì)象。
2.就緒狀態(tài)(Runnable):線程對(duì)象創(chuàng)建后,其他線程調(diào)用了該對(duì)象的start方法。該狀態(tài)的線程位于
可運(yùn)行線程池中,變得可運(yùn)行,等待獲取CPU的使用權(quán)。
3.運(yùn)行狀態(tài)(Running):就緒狀態(tài)的線程獲取了CPU,執(zhí)行程序代碼。
4.阻塞狀態(tài)(Blocked):阻塞狀態(tài)是線程因?yàn)槟撤N原因放棄CPU使用權(quán),暫時(shí)停止運(yùn)行。直到線程進(jìn)
入就緒狀態(tài),才有機(jī)會(huì)轉(zhuǎn)到運(yùn)行狀態(tài)。
5.死亡狀態(tài)(Dead):線程執(zhí)行完了或者因異常退出了run方法,該線程結(jié)束生命周期.
sleep。、wait。、join。、yield。的區(qū)別
1.鎖池
所有需要競(jìng)爭(zhēng)同步鎖的線程都會(huì)放在鎖池當(dāng)中,比如當(dāng)前對(duì)象的鎖已經(jīng)被其中一個(gè)線程得到,則其他線
程需要在這個(gè)鎖池進(jìn)行等待,當(dāng)前面的線程釋放同步鎖后鎖池中的線程去競(jìng)爭(zhēng)同步鎖,當(dāng)某個(gè)線程得到
后會(huì)進(jìn)入就緒隊(duì)列進(jìn)行等待cpu資源分配。
2.等待池
當(dāng)我們調(diào)用wait()方法后,線程會(huì)放到等待池當(dāng)中,等待池的線程是不會(huì)去競(jìng)爭(zhēng)同步鎖。只有調(diào)用了
notify()或notifyAII()后等待池的線程才會(huì)開(kāi)始去競(jìng)爭(zhēng)鎖,notify()是隨機(jī)從等待池選出一個(gè)線程放
到鎖池,而notifyAI1(堤將等待池的所有線程放到鎖池當(dāng)中
1、sleep是Thread類的靜態(tài)本地方法,wait則是。bject類的本地方法。
2、sleep方法不會(huì)釋放lock,但是wait會(huì)釋放,而且會(huì)加入到等待隊(duì)列中.
sleep就是把cpu的執(zhí)行資格和執(zhí)行權(quán)釋放出去,不再運(yùn)行此線程,當(dāng)定時(shí)時(shí)間結(jié)束再取回cpu資源,參與cpu
的調(diào)度,獲取到cpu資源后就可以繼續(xù)運(yùn)行了。而如果sleep時(shí)該線程有鎖,那么sleep不會(huì)釋放這個(gè)鎖,而
是把鎖帶著進(jìn)入了凍結(jié)狀態(tài),也就是說(shuō)其他需要這個(gè)鎖的線程根本不可能獲取到這個(gè)鎖.也就是說(shuō)無(wú)法執(zhí)行程
序。如果在睡眠期間其他線程調(diào)用了這個(gè)線程的interrupt方法,那么這個(gè)線程也會(huì)拋出
interruptexception異常返回,這點(diǎn)和wait是一樣的。
3、sleep方法不依賴于同步器synchronized,但是wait需要依賴synchronized關(guān)健字。
4、sleep不需要被喚醒(休眠之后推出阻塞),但是wait需要(不指定時(shí)間需要被別人中斷)。
5、sleep一般用于當(dāng)前線程休眠,或者輪循暫停操作,wait則多用于多線程之間的通信。
6、sleep會(huì)讓出CPU執(zhí)行時(shí)間且強(qiáng)制上下文切換,而wait則不一定,wait后可能還是有機(jī)會(huì)重新競(jìng)
爭(zhēng)到鎖繼續(xù)執(zhí)行的。
yield()執(zhí)行后線程直接進(jìn)入就緒狀態(tài),馬上釋放了cpu的執(zhí)行權(quán),但是依然保留了cpu的執(zhí)行資格,
所以有可能cpu下次進(jìn)行線程調(diào)度還會(huì)讓這個(gè)線程獲取到執(zhí)行權(quán)繼續(xù)執(zhí)行
join()執(zhí)行后線程進(jìn)入阻塞狀態(tài),例如在線程B中調(diào)用線程A的join(),那線程B會(huì)進(jìn)入到阻塞隊(duì)
列,直到線程A結(jié)束或中斷線程
publicstaticvoidmain(String[]args)throwsinterruptedException{
Threadtl=newThread(newRunnable(){
?Override
publicvoidrun(){
try(
Thread.sleep(3000);
}catch(InterruptedExceptione){
e.printStackTrace();
)
System.out.println(H22222222");
}
});
tl.startf);
tl.joinQ;
//這行代碼必須要等tl全部執(zhí)行完畢,才會(huì)執(zhí)行
System.out.printl;
)
22222222
1111
對(duì)線程安全的理解
不是線程安全、應(yīng)該是內(nèi)存安全,堆是共享內(nèi)存,可以被所有線程訪問(wèn)
當(dāng)多個(gè)線程訪問(wèn)一個(gè)對(duì)象時(shí),如果不用進(jìn)行額外的同步控制或其他的協(xié)調(diào)操作,調(diào)用這個(gè)對(duì)象的行為都可以獲
得正確的結(jié)果,我們就說(shuō)這個(gè)對(duì)象是線程安全的
堆是進(jìn)程和線程共有的空間,分全局堆和局部堆。全局堆就是所有沒(méi)有分配的空間,局部堆就是用戶分
配的空間。堆在操作系統(tǒng)對(duì)進(jìn)程初始化的時(shí)候分配,運(yùn)行過(guò)程中也可以向系統(tǒng)要額外的堆,但是用完了
要還給操作系統(tǒng),要不然就是內(nèi)存泄漏。
在Java中,堆是Java虛擬機(jī)所管理的內(nèi)存中最大的一塊,是所有線程共享的一塊內(nèi)存區(qū)域,在虛
擬機(jī)啟動(dòng)時(shí)創(chuàng)建。堆所存在的內(nèi)存區(qū)域的唯一目的就是存放對(duì)象實(shí)例,幾乎所有的對(duì)象實(shí)例以及
數(shù)組都在這里分配內(nèi)存。
棧是每個(gè)線程獨(dú)有的,保存其運(yùn)行狀態(tài)和局部自動(dòng)變量的。棧在線程開(kāi)始的時(shí)候初始化,每個(gè)線程的棧
互相獨(dú)立,因此,棧是線程安全的。操作系統(tǒng)在切換線程的時(shí)候會(huì)自動(dòng)切換棧。??臻g不需要在高級(jí)語(yǔ)
言里面顯式的分配和釋放.
目前主流操作系統(tǒng)都是多任務(wù)的,即多個(gè)進(jìn)程同時(shí)運(yùn)行。為了保證安全,每個(gè)進(jìn)程只能訪問(wèn)分配給自己
的內(nèi)存空間,而不能訪問(wèn)別的進(jìn)程的,這是由操作系統(tǒng)保障的。
在每個(gè)進(jìn)程的內(nèi)存空間中都會(huì)有一塊特殊的公共區(qū)域,通常稱為堆(內(nèi)存)。進(jìn)程內(nèi)的所有線程都可以
訪問(wèn)到該區(qū)域,這就是造成問(wèn)題的潛在原因。
Thread、Runable的區(qū)別
Thread和Runnable的實(shí)質(zhì)是繼承關(guān)系,沒(méi)有可比性。無(wú)論使用Runnable還是Thread,都會(huì)new
Thread,然后執(zhí)行run方法。用法上,如果有復(fù)雜的線程操作需求,那就選擇繼承Thread,如果只是簡(jiǎn)
單的執(zhí)行一個(gè)任務(wù),那就實(shí)現(xiàn)runnable。
//會(huì)賣(mài)出多一倍的票
publicclassTest{
publicstaticvoidmain(String[]args){
//TODOAuto-generatedmethodstub
newMyThread().start();
newMyThread().start();
)
staticclassMyThreadextendsThread{
privateintticket=5;
publicvoidrun(){
while(true){
System.out.printlnC'Threadticket="+ticket--);
if(ticket<0){
break;
)
)
)
)
)
//正常賣(mài)出
publicclassTest2{
publicstaticvoidmain(String[]args){
//TODOAuto-generatedmethodstub
MyThread2mt=newMyThread2();
newThread(mt).start();
newThread(mt).start();
}
staticclassMyThread2implementsRunnable{
privateintticket=5;
publicvoidrun(){
while(true){
System.out.printlnC'Runnableticket="+ticket--);
if(ticket<0){
break;
}
)
)
)
)
原因是:MyThread創(chuàng)建了兩個(gè)實(shí)例,自然會(huì)賣(mài)出兩倍,屬于用法錯(cuò)誤
對(duì)守護(hù)線程的理解
守護(hù)線程:為所有非守護(hù)線程提供服務(wù)的線程;任何一個(gè)守護(hù)線程都是整個(gè)JVM中所有非守護(hù)線程的保
姆;
守護(hù)線程類似于整個(gè)進(jìn)程的一個(gè)默默無(wú)聞的小嘍嘍;它的生死無(wú)關(guān)重要,它卻依賴整個(gè)進(jìn)程而運(yùn)行;哪
天其他線程結(jié)束了,沒(méi)有要執(zhí)行的了,程序就結(jié)束了,理都沒(méi)理守護(hù)線程,就把它中斷了;
注意:由于守護(hù)線程的終止是自身無(wú)法控制的,因此千萬(wàn)不要把10、File等重要操作邏輯分配給它;因
為它不靠譜;
守護(hù)線程的作用是什么?
舉例,GC垃圾回收線程:就是一個(gè)經(jīng)典的守護(hù)線程,當(dāng)我們的程序中不再有任何運(yùn)行的Thread,程序就
不會(huì)再產(chǎn)生垃圾,垃圾回收器也就無(wú)事可做,所以當(dāng)垃圾回收線程是JVM上僅剩的線程時(shí),垃圾回收線
程會(huì)自動(dòng)離開(kāi)。它始終在低級(jí)別的狀態(tài)中運(yùn)行,用于實(shí)時(shí)監(jiān)控和管理系統(tǒng)中的可回收資源。
應(yīng)用場(chǎng)景:(1)來(lái)為其它線程提供服務(wù)支持的情況;(2)或者在任1可情況下,程序結(jié)束時(shí),這個(gè)線
程必須正常且立刻關(guān)閉,就可以作為守護(hù)線程來(lái)使用;反之,如果一個(gè)正在執(zhí)行某個(gè)操作的線程必須要
正確地關(guān)閉掉否則就會(huì)出現(xiàn)不好的后果的話,那么這個(gè)線程就不能是守護(hù)線程,而是用戶線程。通常都
是些關(guān)鍵的事務(wù),比方說(shuō),數(shù)據(jù)庫(kù)錄入或者更新,這些操作都是不能中斷的。
thread.setDaemon(true)必須在thread.start。之前設(shè)置,否則會(huì)跑出一個(gè)
HlegalThreadStateException異常。你不能把正在運(yùn)行的常規(guī)線程設(shè)置為守護(hù)線程。
在Daemon線程中產(chǎn)生的新線程也是Daemon的。
守護(hù)線程不能用于去訪問(wèn)固有資源,比如讀寫(xiě)操作或者計(jì)算邏輯。因?yàn)樗鼤?huì)在任何時(shí)候甚至在一個(gè)操作
的中間發(fā)生中斷。
Java自帶的多線程框架,比如ExecutorService,會(huì)將守護(hù)線程轉(zhuǎn)換為用戶線程,所以如果要使用后臺(tái)線
程就不能用Java的線程池。
ThreadLocal的原理和使用場(chǎng)景
每一個(gè)Thread對(duì)象均含有一個(gè)ThreadLocalMap類型的成員變量threadLocals,它存儲(chǔ)本線程中所
有ThreadLocal對(duì)象及其對(duì)應(yīng)的值
ThreadLocalMap由一個(gè)個(gè)Entry對(duì)象構(gòu)成
Entry繼承自weakReference<ThreadLoca1<?>>,一個(gè)Entry由ThreadLocal對(duì)象和Object構(gòu)
成。由此可見(jiàn),Entry的key是ThreadLocal對(duì)象,并且是一個(gè)弱引用。當(dāng)沒(méi)指向key的強(qiáng)引用后,該
key就會(huì)被垃圾收集器回收
當(dāng)執(zhí)行set方法時(shí),ThreadLocal首先會(huì)獲取當(dāng)前線程對(duì)象,然后獲取當(dāng)前線程的ThreadLocalMap對(duì)
象。再以當(dāng)前ThreadLocal對(duì)象為key,將值存儲(chǔ)進(jìn)ThreadLocalMap對(duì)象中。
get方法執(zhí)行過(guò)程類似。ThreadLocaI首先會(huì)獲取當(dāng)前線程對(duì)象,然后獲取當(dāng)前線程的ThreadLocalM叩
對(duì)象。再以當(dāng)前ThreadLocal對(duì)象為key,獲取對(duì)應(yīng)的value。
由于每一條線程均含有各自私有的ThreadLocalMap容器,這些容器相互獨(dú)立互不影響,因此不會(huì)存在
線程安全性問(wèn)題,從而也無(wú)需使用同步機(jī)制來(lái)保證多條線程訪問(wèn)容器的互斥性.
使用場(chǎng)景:
1、在進(jìn)行對(duì)象跨層傳遞的時(shí)候,使用ThreadLocal可以避免多次傳遞,打破層次間的約束。
2、線程間數(shù)據(jù)隔離
3、進(jìn)行事務(wù)操作,用于存儲(chǔ)線程事務(wù)信息。
4、數(shù)據(jù)庫(kù)連接,Session會(huì)話管理。
Spring框架在事務(wù)開(kāi)始時(shí)會(huì)給當(dāng)前線程綁定一個(gè)JdbcConnect!on,在整個(gè)事務(wù)過(guò)程都是使用該線程綁定的
connection來(lái)執(zhí)行數(shù)據(jù)庫(kù)操作,實(shí)現(xiàn)了事務(wù)的隔離性。Spring框架里面就是用的ThreadLocal來(lái)實(shí)現(xiàn)這種
隔離
ThreadLocal內(nèi)存泄露原因,如何避免
內(nèi)存泄露為程序在申請(qǐng)內(nèi)存后,無(wú)法釋放已申請(qǐng)的內(nèi)存空間,一次內(nèi)存泄露危害可以忽略,但內(nèi)存泄露
堆積后果很?chē)?yán)重,無(wú)論多少內(nèi)存,遲早會(huì)被占光,
不再會(huì)被使用的對(duì)象或者變量占用的內(nèi)存不能被回收,就是內(nèi)存泄露。
強(qiáng)引用:使用最普遍的引用(new),一個(gè)對(duì)象具有強(qiáng)引用,不會(huì)被垃圾回收器回收。當(dāng)內(nèi)存空間不足,
Java虛擬機(jī)寧愿拋出OutOfMemoryErro儲(chǔ)誤,使程序異常終止,也不回收這種對(duì)象。
如果想取消強(qiáng)引用和某個(gè)對(duì)象之間的關(guān)聯(lián),可以顯式地將引用賦值為null,這樣可以使|VM在合適的時(shí)
間就會(huì)回收該對(duì)象。
弱引用:JVM進(jìn)行垃圾回收時(shí),無(wú)論內(nèi)存是否充足,都會(huì)回收被弱引用關(guān)聯(lián)的對(duì)象。在java中,用
java.Iang.ref.WeakReference類來(lái)表示??梢栽诰彺嬷惺褂萌跻?。
ThreadLocal的實(shí)現(xiàn)原理,每一個(gè)Thread維護(hù)一個(gè)ThreadLocalM叩,key為使用弱弓|用的ThreadLocal
實(shí)例,value為線程變量的副本
hreadLocalM叩使用ThreadLocal的弱弓|用作為key,如果―HhreadLocal不存在外部強(qiáng)引用時(shí),
Key(ThreadLocal)勢(shì)必會(huì)被GC回收,這樣就會(huì)導(dǎo)致ThreadLocalM叩中key為null,而value還存在著強(qiáng)
引用,只有thead線程退出以后,value的強(qiáng)引用鏈條才會(huì)斷掉,但如果當(dāng)前線程再遲遲不結(jié)束的話,這
些key為null的Entry的value就會(huì)一直存在一條強(qiáng)引用鏈(紅色鏈條)
key使用強(qiáng)引用
當(dāng)hreadLocalM叩的key為強(qiáng)引用回收ThreadLocal時(shí),因?yàn)門(mén)hreadLocalM叩還持有ThreadLocal的強(qiáng)
引用,如果沒(méi)有手動(dòng)刪除,ThreadLocal不會(huì)被回收,導(dǎo)致Entry內(nèi)存泄漏。
key使用弱引用
當(dāng)仆0€13211\/^的1<6丫為弱弓|用回收什0(^321時(shí),由于ThreadLocalMap持有ThreadLocal的弱
引用,即使沒(méi)有手動(dòng)刪除,ThreadLocal也會(huì)被回收。當(dāng)key為null,在下一次ThreadLocalM叩調(diào)用
set(),get(),remove。方法的時(shí)候會(huì)被清除value值。
因此,ThreadLocal內(nèi)存泄漏的根源是:由于ThreadLocalM叩的生命周期跟Thread一樣長(zhǎng),如果沒(méi)有
手動(dòng)刪除對(duì)應(yīng)key就會(huì)導(dǎo)致內(nèi)存泄漏,而不是因?yàn)槿跻谩?/p>
ThreadLocaI正確的使用方法
?每次使用完ThreadLocal都調(diào)用它的remove。方法清除數(shù)據(jù)
?將ThreadLocal變量定義成privatestatic,這樣就一直存在ThreadLocal的強(qiáng)引用,也就能保證任
何時(shí)候都能通過(guò)ThreadLocal的弱引用訪問(wèn)到Entry的value值,進(jìn)而清除掉。
并發(fā)、并行、串行的區(qū)別
串行在時(shí)間上不可能發(fā)生重疊,前一個(gè)任務(wù)沒(méi)搞定,下一個(gè)任務(wù)就只能等著
并行在時(shí)間上是重疊的,兩個(gè)任務(wù)在同一時(shí)刻互不干擾的同時(shí)執(zhí)行。
并發(fā)允許兩個(gè)任務(wù)彼此干擾.統(tǒng)一時(shí)間點(diǎn)、只有一個(gè)任務(wù)運(yùn)行,交替執(zhí)行
并發(fā)的三大特性
?原子性
原子性是指在一個(gè)操作中cpu不可以在中途暫停然后再調(diào)度,即不被中斷操作,要不全部執(zhí)行完成,要
不都不執(zhí)行。就好比轉(zhuǎn)賬,從賬戶A向賬戶B轉(zhuǎn)1000元,那么必然包括2個(gè)操作:從賬戶A減去1000元,
往賬戶B加上1000元。2個(gè)操作必須全部完成。
privatelongcount=0;
publicvoidcalc(){
count++;
)
?1:將count從主存讀到工作內(nèi)存中的副本中
?2:+1的運(yùn)算
?3:將結(jié)果寫(xiě)入工作內(nèi)存
?4:將工作內(nèi)存的值刷回主存(什么時(shí)候刷入由操作系統(tǒng)決定,不確定的)
那程序中原子性指的是最小的操作單元,比如自增操作,它本身其實(shí)并不是原子性操作,分了3步的,
包括讀取變量的原始值、進(jìn)行加1操作、寫(xiě)入工作內(nèi)存。所以在多線程中,有可能一個(gè)線程還沒(méi)自增
完,可能才執(zhí)行到第二部,另一個(gè)線程就已經(jīng)讀取了值,導(dǎo)致結(jié)果錯(cuò)誤。那如果我們能保證自增操作是
一個(gè)原子性的操作,那么就能保證其他線程讀取到的一定是自增后的數(shù)據(jù)。
關(guān)鍵字:synchronized
?可見(jiàn)性
當(dāng)多個(gè)線程訪問(wèn)同一個(gè)變量時(shí),一個(gè)線程修改了這個(gè)變量的值,其他線程能夠立即看得到修改的值。
若兩個(gè)線程在不同的cpu,那么線程1改變了i的值還沒(méi)刷新到主存,線程2又使用了i,那么這個(gè)i值肯定
還是之前的,線程1對(duì)變量的修改線程沒(méi)看到這就是可見(jiàn)性問(wèn)題。
//線程1
booleanstop=false;
while(!stop){
doSomething();
)
//線程2
stop=true;
如果線程2改變了stop的值,線程1一定會(huì)停止嗎?不一定。當(dāng)線程2更改了stop變量的值之后,但是還
沒(méi)來(lái)得及寫(xiě)入主存當(dāng)中,線程2轉(zhuǎn)去做其他事情了,那么線程1由于不知道線程2對(duì)stop變量的更改,因
此還會(huì)一直循環(huán)下去。
關(guān)鍵字:volatile,synchronized,final
?有序性
虛擬機(jī)在進(jìn)行代碼編譯時(shí),對(duì)于那些改變順序之后不會(huì)對(duì)最終結(jié)果造成影響的代碼,虛擬機(jī)不一定會(huì)按
照我們寫(xiě)的代碼的順序來(lái)執(zhí)行,有可能將他們重排序。實(shí)際上,對(duì)于有些代碼進(jìn)行重排序之后,雖然對(duì)
變量的值沒(méi)有造成影響,但有可能會(huì)出現(xiàn)線程安全問(wèn)題。
inta=0;
boolflag=false;
publicvoidwrite(){
a=2;//I
flag=true;//2
}
publicvoidmultiplyO{
if(flag){〃3
intret=a*a;//4
)
)
write方法里的1和2做了重排序,線程1先對(duì)flag賦值為true,隨后執(zhí)行到線程2,ret直接計(jì)算出結(jié)果,
再到線程1,這時(shí)候a才賦值為2彳艮明顯遲了一步
關(guān)鍵字:volatile,synchronized
volatile本身就包含了禁止指令重排序的語(yǔ)義,而synchronized關(guān)鍵字是由"一個(gè)變量在同一時(shí)刻只允許
T線程對(duì)其進(jìn)行l(wèi)ock操作這條規(guī)則明確的。
synchronized關(guān)鍵字同時(shí)滿足以上三種特性,但是vaatile關(guān)鍵字不滿足原子性。
在某些情況下,vMatile的同步機(jī)制的性能確實(shí)要優(yōu)于鎖(使用synchronized關(guān)鍵字或
java.util.concurrent包里面的鎖),因?yàn)関olatile的總開(kāi)銷(xiāo)要比鎖低。
我們判斷使用volatile還是加鎖的唯一依據(jù)就是volatile的語(yǔ)義能否滿足使用的場(chǎng)景(原子性)
volatile
1.保證被volatile修飾的共享變量對(duì)所有線程總是可見(jiàn)的,也就是當(dāng)一個(gè)線程修改了一個(gè)被volatile修
飾共享變量的值,新值總是可以被其他線程立即得知。
//線程1
booleanstop=false;
while(!stop){
dosomething();
}
//線程2
stop=true;
如果線程2改變了stop的值,線程1一定會(huì)停止嗎?不一定。當(dāng)線程2更改了stop變量的值之后,但
是還沒(méi)來(lái)得及寫(xiě)入主存當(dāng)中,線程2轉(zhuǎn)去做其他事情了,那么線程1由于不知道線程2對(duì)stop變量的
更改,因此還會(huì)一直循環(huán)下去。
2.禁止指令重排序優(yōu)化。
1nta=0;
boolflag=false;
publicvoidwrite(){
a=2;//I
flag=true;//2
)
publicvoidmultiply(){
if(flag){//3
intret=a*a;//4
)
}
write方法里的1和2做了重排序,線程1先對(duì)flag賦值為true,隨后執(zhí)行到線程2,ret直接計(jì)算出結(jié)果,
再到線程1,這時(shí)候a才賦值為2彳艮明顯遲了一步。
但是用volatile修飾之后就變得不一樣了
第一:使用volatile關(guān)鍵字會(huì)強(qiáng)制將修改的值立即寫(xiě)入主存;
第二:使用volatile關(guān)鍵字的話,當(dāng)線程2進(jìn)行修改時(shí),會(huì)導(dǎo)致線程1的工作內(nèi)存中緩存變量stop的緩存
行無(wú)效(反映到硬件層的話,就是CPU的L1或者L2緩存中對(duì)應(yīng)的緩存行無(wú)效);
第三:由于線程1的工作內(nèi)存中緩存變量stop的緩存行無(wú)效,所以線程1再次讀取變量stop的值時(shí)會(huì)去主
存讀取.
inc++;其實(shí)是兩個(gè)步驟,先加加,然后再賦值。不是原子性操作,所以vogtile不能保證線程安全.
為什么用線程池?解釋下線程池參數(shù)?
1、降低資源消耗;提高線程利用率,降低創(chuàng)建和銷(xiāo)毀線程的消耗。
2、提高響應(yīng)速度;任務(wù)來(lái)了,直接有線程可用可執(zhí)行,而不是先創(chuàng)建線程,再執(zhí)行。
3、提高線程的可管理性;線程是稀缺資源,使用線程池可以統(tǒng)一分配調(diào)優(yōu)監(jiān)控。
?corePoolSize代表核心線程數(shù),也就是正常情況下創(chuàng)建工作的線程數(shù),這些線程創(chuàng)建后并不會(huì)
消除,而是一種常駐線程
?maxinumpoolSize代表的是最大線程數(shù),它與核心線程數(shù)相對(duì)應(yīng),表示最大允許被創(chuàng)建的線程
數(shù),比如當(dāng)前任務(wù)較多,將核心線程數(shù)都用完了,還無(wú)法滿足需求時(shí),此時(shí)就會(huì)創(chuàng)建新的線程,但
是線程池內(nèi)線程總數(shù)不會(huì)超過(guò)最大線程數(shù)
?keepAliveTime,unit表示超出核心線程數(shù)之外的線程的空閑存活時(shí)間,也就是核心線程不會(huì)
消除,但是超出核心線程數(shù)的部分線程如果空閑一定的時(shí)間則會(huì)被消除,我們可以通過(guò)
setKeepAliveTime來(lái)設(shè)置空閑時(shí)間
?workQueue用來(lái)存放待執(zhí)行的任務(wù),假設(shè)我們現(xiàn)在核心線程都已被使用,還有任務(wù)進(jìn)來(lái)則全部放
入隊(duì)列,直到整個(gè)隊(duì)列被放滿但任務(wù)還再持續(xù)進(jìn)入則會(huì)開(kāi)始創(chuàng)建新的線程
?ThreadFactory實(shí)際上是一個(gè)線程工廠,用來(lái)生產(chǎn)線程執(zhí)行任務(wù)。我們可以選擇使用默認(rèn)的創(chuàng)建
工廠,產(chǎn)生的線程都在同一個(gè)組內(nèi),擁有相同的優(yōu)先級(jí),且都不是守護(hù)線程。當(dāng)然我們也可以選擇
自定義線程工廠,一般我們會(huì)根據(jù)業(yè)務(wù)來(lái)制定不同的線程工廠
?Handler任務(wù)拒絕策略,有兩種情況,第一種是當(dāng)我們調(diào)用shutdown等方法關(guān)閉線程池后,這
時(shí)候即使線程池內(nèi)部還有沒(méi)執(zhí)行完的任務(wù)正在執(zhí)行,但是由于線程池已經(jīng)關(guān)閉,我們?cè)倮^續(xù)想線程
池提交任務(wù)就會(huì)遭到拒絕。另一種情況就是當(dāng)達(dá)到最大線程數(shù),線程池已經(jīng)沒(méi)有能力繼續(xù)處理新提
交的任務(wù)時(shí),這是也就拒絕
簡(jiǎn)述線程池處理流程
線程池執(zhí)行任務(wù)
已達(dá)到
根據(jù)拒絕策略處理
任務(wù)
線程池中阻塞隊(duì)列的作用?為什么是先添加列隊(duì)而不是先
創(chuàng)建最大線程?
1、一般的隊(duì)列只能保證作為一個(gè)有限長(zhǎng)度的緩沖區(qū),如果超出了緩沖長(zhǎng)度,就無(wú)法保留當(dāng)前的任務(wù)
了,阻塞隊(duì)列通過(guò)阻塞可以保留住當(dāng)前想要繼續(xù)入隊(duì)的任務(wù)。
阻塞隊(duì)列可以保證任務(wù)隊(duì)列中沒(méi)有任務(wù)時(shí)阻塞獲取任務(wù)的線程,使得線程進(jìn)入wait狀態(tài),釋放cpu資
源。
阻塞隊(duì)列自帶阻塞和喚醒的功能,不需要額外處理,無(wú)任務(wù)執(zhí)行時(shí),線程池利用阻塞隊(duì)列的take方法掛
起,從而維持核心線程的存活、不至于一直占用cpu資源
2、在創(chuàng)建新線程的時(shí)候,是要獲取全局鎖的,這個(gè)時(shí)候其它的就得阻塞,影響了整體效率。
就好比一個(gè)企業(yè)里面有10個(gè)(core)正式工的名額,最多招10個(gè)正式工,要是任務(wù)超過(guò)正式工人數(shù)
(task>core)的情況下,工廠領(lǐng)導(dǎo)(線程池)不是首先擴(kuò)招工人,還是這10人,但是任務(wù)可以稍微積
壓一下,即先放到隊(duì)列去(代價(jià)低)。10個(gè)正式工慢慢干,遲早會(huì)干完的,要是任務(wù)還在繼續(xù)增加,超
過(guò)正式工的加班忍耐極限了(隊(duì)列滿了),就的招外包幫忙了(注意是臨時(shí)工)要是正式工加上外包還
是不能完成任務(wù),那新來(lái)的任務(wù)就會(huì)被領(lǐng)導(dǎo)拒絕了(線程池的拒絕策略)。
線程池中線程復(fù)用原理
線程池將線程和任務(wù)進(jìn)行解耦,線程是線程,任務(wù)是任務(wù),擺脫了之前通過(guò)Thread創(chuàng)建線程時(shí)的
一個(gè)線程必須對(duì)應(yīng)一個(gè)任務(wù)的限制。
在線程池中,同一個(gè)線程可以從阻塞隊(duì)列中不斷獲取新任務(wù)來(lái)執(zhí)行,其核心原理在于線程池對(duì)
Thread進(jìn)行了封裝,并不是每次執(zhí)行任務(wù)都會(huì)調(diào)用Thread.start()來(lái)創(chuàng)建新線程,而是讓每個(gè)線程去
執(zhí)行一個(gè)“循環(huán)任務(wù)",在這個(gè)"循環(huán)任務(wù)”中不停檢查是否有任務(wù)需要被執(zhí)行,如果有則直接執(zhí)行,也就
是調(diào)用任務(wù)中的run方法,將run方法當(dāng)成一個(gè)普通的方法執(zhí)行,通過(guò)這種方式只使用固定的線程就
將所有任務(wù)的run方法串聯(lián)起來(lái)。
spring
如何實(shí)現(xiàn)一個(gè)IOC容器
1、配置文件配置包掃描路徑
2、遞歸包掃描獲取class文件
3、反射、確定需要交給I0C管理的類
4、對(duì)需要注入的類進(jìn)行依賴注入
?配置文件中指定需要掃描的包路徑
?定義一些注解,分別表示訪問(wèn)控制層、業(yè)務(wù)服務(wù)層、數(shù)據(jù)持久層、依賴注入注解、獲取配置文件注
解
?從配置文件中獲取需要掃描的包路徑,獲取到當(dāng)前路徑下的文件信息及文件夾信息,我們將當(dāng)前路
徑下所有以.class結(jié)尾的文件添加到一個(gè)Set集合中進(jìn)行存儲(chǔ)
?遍歷這個(gè)set集合,獲取在類上有指定注解的類,并將其交給I0C容器,定義一個(gè)安全的Map用來(lái)
存儲(chǔ)這些對(duì)象
?遍歷這個(gè)I0C容器,獲取到每一個(gè)類的實(shí)例,判斷里面是有有依賴其他的類的實(shí)例,然后進(jìn)行遞歸
注入
spring是什么?
輕量級(jí)的開(kāi)源的J2EE框架。它是一個(gè)容器框架,用來(lái)裝javabean(java對(duì)象),中間層框架(萬(wàn)能膠)
可以起一個(gè)連接作用,比如說(shuō)把Struts和hibernate粘合在一起運(yùn)用,可以讓我們的企業(yè)開(kāi)發(fā)更快、更簡(jiǎn)
:士
/口
Spring是一個(gè)輕量級(jí)的控制反轉(zhuǎn)(1。。和面向切面(AOP)的容器框架
-從大小與開(kāi)銷(xiāo)兩方面而言Spring都是輕量級(jí)的。
-通過(guò)控制反轉(zhuǎn)(1。0的技術(shù)達(dá)到松耦合的目的
--提供了面向切面編程的豐富支持,允許通過(guò)分離應(yīng)用的業(yè)務(wù)邏輯與系統(tǒng)級(jí)服務(wù)進(jìn)行內(nèi)聚性的
開(kāi)發(fā)
-包含并管理應(yīng)用對(duì)象(Bean)的配置和生命周期,這個(gè)意義上是一個(gè)容器。
-將簡(jiǎn)單的組件配置、組合成為復(fù)雜的應(yīng)用,這個(gè)意義上是一個(gè)框架。
談?wù)勀銓?duì)AOP的理解
系統(tǒng)是由許多不同的組件所組成的,每一個(gè)組件各負(fù)責(zé)一塊特定功能。除了實(shí)現(xiàn)自身核心功能之外,這
些組件還經(jīng)常承擔(dān)著額外的職責(zé)。例如日志'事務(wù)管理和安全這樣的核心服務(wù)經(jīng)常融入到自身具有核心
業(yè)務(wù)邏輯的組件中去。這些系統(tǒng)服務(wù)經(jīng)常被稱為橫切關(guān)注點(diǎn),因?yàn)樗鼈儠?huì)跨越系統(tǒng)的多個(gè)組件。
當(dāng)我們需要為分散的對(duì)象引入公共行為的時(shí)候,OOP則顯得無(wú)能為力。也就是說(shuō),OOP允許你定義從
上到下的關(guān)系,但并不適合定義從左到右的關(guān)系.例如日志功能。
日志代碼往往水平地散布在所有對(duì)象層次中,而與它所散布到的對(duì)象的核心功能毫無(wú)關(guān)系。
在OOP設(shè)計(jì)中,它導(dǎo)致了大量代碼的重復(fù),而不利于各個(gè)模塊的重用。
AOP:將程序中的交叉業(yè)務(wù)邏輯(比如安全,日志,事務(wù)等),封裝成一個(gè)切面,然后注入到目標(biāo)對(duì)象
(具體業(yè)務(wù)邏輯)中去。AOP可以對(duì)某個(gè)對(duì)象或某些對(duì)象的功能進(jìn)行增強(qiáng),比如對(duì)象中的方法進(jìn)行增
強(qiáng),可以在執(zhí)行某個(gè)方法之前額外的做一些事情,在某個(gè)方法執(zhí)行之后額外的做一些事情
談?wù)勀銓?duì)IOC的理解
容器概念、控制反轉(zhuǎn)、依賴注入
ioc容器:實(shí)際上就是個(gè)m叩(key,value),里面存的是各種對(duì)象(在xml里配置的bean節(jié)點(diǎn)、
、在項(xiàng)目啟動(dòng)的時(shí)候會(huì)讀取配置文件里面的
◎repository?service,@controllerx?component),
bean節(jié)點(diǎn),根據(jù)全限定類名使用反射創(chuàng)建對(duì)象放到m叩里、掃描到打上上述注解的類還是通過(guò)反射創(chuàng)
建對(duì)象放到m叩里。
這個(gè)時(shí)候map里就有各種對(duì)象了,接下來(lái)我們?cè)诖a里需要用到里面的對(duì)象時(shí),再通過(guò)DI注入
(autowired、resource等注解,xml里bean節(jié)點(diǎn)內(nèi)的ref屬性,項(xiàng)目啟動(dòng)的時(shí)候會(huì)讀取xml節(jié)點(diǎn)ref屬性
根據(jù)id注入,也會(huì)掃描這些注解,根據(jù)類型或id注入;id就是對(duì)象名)。
控制反轉(zhuǎn):
沒(méi)有引入QC容器之前,對(duì)象A依賴于對(duì)象B,那么對(duì)象A在初始化或者運(yùn)行到某一點(diǎn)的時(shí)候,自己必須
主動(dòng)去創(chuàng)建對(duì)象B或者使用已經(jīng)創(chuàng)建的對(duì)象B。無(wú)論是創(chuàng)建還是使用對(duì)象B,控制權(quán)都在自己手上。
引入IOC容器之后,對(duì)象A與對(duì)象B之間失去了直接聯(lián)系,當(dāng)對(duì)象A運(yùn)行到需要對(duì)象B的時(shí)候,IOC容器會(huì)
主動(dòng)創(chuàng)建一個(gè)對(duì)象B注入到對(duì)象A需要的地方.
通過(guò)前后的對(duì)比,不難看出來(lái):對(duì)象A獲得依賴對(duì)象B的過(guò)程,由主動(dòng)行為變?yōu)榱吮粍?dòng)行為,控制權(quán)顛倒
過(guò)來(lái)了,這就是"控制反轉(zhuǎn)’這個(gè)名稱的由來(lái)。
全部對(duì)象的控制權(quán)全部上繳給"第三方"IOC容器,所以,IOC容器成了整個(gè)系統(tǒng)的關(guān)鍵核心,它起到了一
種類似“粘合劑”的作用,把系統(tǒng)中的所有對(duì)象粘合在一起發(fā)揮作用,如果沒(méi)有這個(gè)“粘合劑”,對(duì)象與對(duì)
象之間會(huì)彼此失去聯(lián)系,這就是有人把IOC容器比喻成"粘合劑”的由來(lái)。
依賴注入:
“獲得依賴對(duì)
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- 體檢錄用合同范本
- 小班常規(guī)建立課題申報(bào)書(shū)
- 漢字課題申報(bào)書(shū)
- 和單位食堂合同范本
- 單方出資合作合同范例
- 合同范本中自動(dòng)簽字
- 叉車(chē)裝卸出租合同范例
- 勞務(wù)分包合同范本全國(guó)
- 優(yōu)化住房公積金政策 助力民生改善
- 合同范本模板采購(gòu)方案
- 《污水中微塑料的測(cè)定 傅里葉變換顯微紅外光譜法》
- 貨物學(xué) 課件1.3貨物的計(jì)量
- 2025四川省資陽(yáng)市人民政府政務(wù)服務(wù)中心招聘4人高頻重點(diǎn)提升(共500題)附帶答案詳解
- 華東師大版初中科學(xué)八年級(jí)上冊(cè)知識(shí)點(diǎn)
- 【MOOC】跨文化思想交流英語(yǔ)-南京理工大學(xué) 中國(guó)大學(xué)慕課MOOC答案
- 部編人教版五年級(jí)下冊(cè)小學(xué)數(shù)學(xué)全冊(cè)教案
- 2024年世界職業(yè)院校技能大賽高職組“聲樂(lè)、器樂(lè)表演組”賽項(xiàng)參考試題庫(kù)(含答案)
- 2024年共青團(tuán)入團(tuán)考試題庫(kù)及答案
- 2024年共青團(tuán)入團(tuán)考試測(cè)試題庫(kù)及答案
- 2024解析:第十二章機(jī)械效率-講核心(原卷版)
- 四川省德陽(yáng)市(2024年-2025年小學(xué)五年級(jí)語(yǔ)文)人教版小升初真題(上學(xué)期)試卷及答案
評(píng)論
0/150
提交評(píng)論