大數(shù)據(jù)面試題
221.List、Map、Set 三個(gè)接口,存取元素時(shí),各有什么特點(diǎn)?
答:List以特定索引來存取元素,可有重復(fù)元素。
Set不能存放重復(fù)元素(用對(duì)象的equals()方法來區(qū)分元素是否重復(fù)) 。Map保存鍵值對(duì)(key-value pair)映射,映射關(guān)系可以是一對(duì)一或多對(duì)一。Set和Map容器都有基于哈希存儲(chǔ)和排序樹(紅黑樹)的兩種實(shí)現(xiàn)版本,基于哈希存儲(chǔ)的版本理論存取時(shí)間復(fù)雜度為O(1),而基于排序樹版本的實(shí)現(xiàn)在插入或刪除元素時(shí)會(huì)按照元素或元素的鍵(key)構(gòu)成排序樹從而達(dá)到排序和去重的效果。?
222.TreeMap和TreeSet在排序時(shí)如何比較元素?Collections工具類中的sort()方法如何比較元素?
答:TreeSet要求存放的對(duì)象所屬的類必須實(shí)現(xiàn)Comparable接口,該接口提供了比較元素的compareTo()方法,當(dāng)插入元素時(shí)會(huì)回調(diào)該方法比較元素的大小。
TreeMap要求存放的鍵值對(duì)映射的鍵必須實(shí)現(xiàn)Comparable接口從而根據(jù)鍵對(duì)元素進(jìn)行排序。
Collections工具類的sort方法有兩種重載的形式,第一種要求傳入的待排序容器中存放的對(duì)象比較實(shí)現(xiàn)Comparable接口以實(shí)現(xiàn)元素的比較;第二種不強(qiáng)制性的要求容器中的元素必須可比較,但是要求傳入第二個(gè)參數(shù),參數(shù)是Comparator接口的子類型 (需要重寫compare方法實(shí)現(xiàn)元素的比較),相當(dāng)于一個(gè)臨時(shí)定義的排序規(guī)則,其實(shí)就是是通過接口注入比較元素大小的算法,也是對(duì)回調(diào)模式的應(yīng)用。
例子1:
Student.java

Test01.java

例子2:
Student.java

Test02.java

多線程:
223.下面程序的運(yùn)行結(jié)果()(選擇一項(xiàng))

A.pingpong
B.pongping
C.pingpong和pongping都有可能
D.都不輸出
答案:B
分析:?jiǎn)?dòng)線程需要調(diào)用start()方法,而t.run()方法,則是使用對(duì)象名.分析:?jiǎn)?dòng)線程需要調(diào)用start()方法,而t.run()方法,則是使用對(duì)象名.
224.下列哪個(gè)方法可用于創(chuàng)建一個(gè)可運(yùn)行的類()
A.public class X implements Runnable{public void run() {……}}
B.public class X extends Thread{public void run() {……}}
C.public class X extends Thread{public int run() {……}}
D.public class X implements Runnable{protected void run() {……}}
答案:AB
分析: 繼承Thread和實(shí)現(xiàn)Runable接口
225.說明類java.lang.ThreadLocal的作用和原理。列舉在哪些程序中見過ThreadLocal的使用?
作用:
要編寫一個(gè)多線程安全(Thread-safe)的程序是困難的,為了讓線程共享資源,必須小心地對(duì)共享資源進(jìn)行同步,同步帶來一定的效能延遲,而另一方面,在處理同步的時(shí)候,又要注意對(duì)象的鎖定與釋放,避免產(chǎn)生死結(jié),種種因素都使得編寫多線程程序變得困難。
嘗試從另一個(gè)角度來思考多線程共享資源的問題,既然共享資源這么困難,那么就干脆不要共享,何不為每個(gè)線程創(chuàng)造一個(gè)資源的復(fù)本。將每一個(gè)線程存取數(shù)據(jù)的行為加以隔離,實(shí)現(xiàn)的方法就是給予每個(gè)線程一個(gè)特定空間來保管該線程所獨(dú)享的資源。
比如:在Hibernate中的Session就有使用。
ThreadLocal的原理
ThreadLocal是如何做到為每一個(gè)線程維護(hù)變量的副本的呢?其實(shí)實(shí)現(xiàn)的思路很簡(jiǎn)單,在ThreadLocal類中有一個(gè)Map,用于存儲(chǔ)每一個(gè)線程的變量的副本。
226.說說樂觀鎖與悲觀鎖
答:悲觀鎖(Pessimistic Lock), 顧名思義,就是很悲觀,每次去拿數(shù)據(jù)的時(shí)候都認(rèn)為別人會(huì)修改,所以每次在拿數(shù)據(jù)的時(shí)候都會(huì)上鎖,這樣別人想拿這個(gè)數(shù)據(jù)就會(huì)block直到它拿到鎖。傳統(tǒng)的關(guān)系型數(shù)據(jù)庫(kù)里邊就用到了很多這種鎖機(jī)制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。
樂觀鎖(Optimistic Lock), 顧名思義,就是很樂觀,每次去拿數(shù)據(jù)的時(shí)候都認(rèn)為別人不會(huì)修改,所以不會(huì)上鎖,但是在更新的時(shí)候會(huì)判斷一下在此期間別人有沒有去更新這個(gè)數(shù)據(jù),可以使用版本號(hào)等機(jī)制。樂觀鎖適用于多讀的應(yīng)用類型,這樣可以提高吞吐量,像數(shù)據(jù)庫(kù)如果提供類似于write_condition機(jī)制的其實(shí)都是提供的樂觀鎖。
兩種鎖各有優(yōu)缺點(diǎn),不可認(rèn)為一種好于另一種,像樂觀鎖適用于寫比較少的情況下,即沖突真的很少發(fā)生的時(shí)候,這樣可以省去了鎖的開銷,加大了系統(tǒng)的整個(gè)吞吐量。但如果經(jīng)常產(chǎn)生沖突,上層應(yīng)用會(huì)不斷的進(jìn)行retry,這樣反倒是降低了性能,所以這種情況下用悲觀鎖就比較合適。
227.在Java中怎么實(shí)現(xiàn)多線程?描述線程狀態(tài)的變化過程。
答:當(dāng)多個(gè)線程訪問同一個(gè)數(shù)據(jù)時(shí),容易出現(xiàn)線程安全問題,需要某種方式來確保資源在某一時(shí)刻只被一個(gè)線程使用。需要讓線程同步,保證數(shù)據(jù)安全線程同步的實(shí)現(xiàn)方案:?同步代碼塊和同步方法,均需要使用synchronized關(guān)鍵字
同步代碼塊:public void makeWithdrawal(int amt) {
synchronized (acct) { }
同步方法:public synchronized void makeWithdrawal(int amt) { }
線程同步的好處:解決了線程安全問題
線程同步的缺點(diǎn):性能下降,可能會(huì)帶來死鎖
228.請(qǐng)寫出多線程代碼使用Thread或者Runnable,并說出兩種的區(qū)別。
方式1:繼承Java.lang.Thread類,并覆蓋run() 方法。優(yōu)勢(shì):編寫簡(jiǎn)單;劣勢(shì):無法繼承其它父類

方式2:實(shí)現(xiàn)Java.lang.Runnable接口,并實(shí)現(xiàn)run()方法。優(yōu)勢(shì):可繼承其它類,多線程可共享同一個(gè)Thread對(duì)象;劣勢(shì):編程方式稍微復(fù)雜,如需訪問當(dāng)前線程,需調(diào)用Thread.currentThread()方法

229.在多線程編程里,wait方法的調(diào)用方式是怎樣的?
答:wait方法是線程通信的方法之一,必須用在 synchronized方法或者synchronized代碼塊中,否則會(huì)拋出異常,這就涉及到一個(gè)“鎖”的概念,而wait方法必須使用上鎖的對(duì)象來調(diào)用,從而持有該對(duì)象的鎖進(jìn)入線程等待狀態(tài),直到使用該上鎖的對(duì)象調(diào)用notify或者notifyAll方法來喚醒之前進(jìn)入等待的線程,以釋放持有的鎖。
230.Java線程的幾種狀態(tài)
答:線程是一個(gè)動(dòng)態(tài)執(zhí)行的過程,它有一個(gè)從產(chǎn)生到死亡的過程,共五種狀態(tài):
新建(new Thread)
當(dāng)創(chuàng)建Thread類的一個(gè)實(shí)例(對(duì)象)時(shí),此線程進(jìn)入新建狀態(tài)(未被啟動(dòng))
例如:Thread t1=new Thread();
就緒(runnable)
線程已經(jīng)被啟動(dòng),正在等待被分配給CPU時(shí)間片,也就是說此時(shí)線程正在就緒隊(duì)列中排隊(duì)等候得到CPU資源。例如:t1.start();
運(yùn)行(running)
線程獲得CPU資源正在執(zhí)行任務(wù)(run()方法),此時(shí)除非此線程自動(dòng)放棄CPU資源或者有優(yōu)先級(jí)更高的線程進(jìn)入,線程將一直運(yùn)行到結(jié)束。
死亡(dead)
當(dāng)線程執(zhí)行完畢或被其它線程殺死,線程就進(jìn)入死亡狀態(tài),這時(shí)線程不可能再進(jìn)入就緒狀態(tài)等待執(zhí)行。
自然終止:正常運(yùn)行run()方法后終止
異常終止:調(diào)用stop()方法讓一個(gè)線程終止運(yùn)行
堵塞(blocked)
由于某種原因?qū)е抡谶\(yùn)行的線程讓出CPU并暫停自己的執(zhí)行,即進(jìn)入堵塞狀態(tài)。
正在睡眠:用sleep(long t) 方法可使線程進(jìn)入睡眠方式。一個(gè)睡眠著的線程在指定的時(shí)間過去可進(jìn)入就緒狀態(tài)。
正在等待:調(diào)用wait()方法。(調(diào)用motify()方法回到就緒狀態(tài))
被另一個(gè)線程所阻塞:調(diào)用suspend()方法。(調(diào)用resume()方法恢復(fù))
231.在Java多線程中,請(qǐng)用下面哪種方式不會(huì)使線程進(jìn)入阻塞狀態(tài)()
A.sleep(? )
B.Suspend(? )
C.wait(? )
D.yield(? )
答案:D
分析:yield會(huì)是線程進(jìn)入就緒狀態(tài)
232.volatile關(guān)鍵字是否能保證線程安全?
答:不能。雖然volatile提供了同步的機(jī)制,但是知識(shí)一種弱的同步機(jī)制,如需要強(qiáng)線程安全,還需要使用synchronized。
Java語言提供了一種稍弱的同步機(jī)制,即volatile變量,用來確保將變量的更新操作通知到其他線程。當(dāng)把變量聲明為volatile類型后,編譯器與運(yùn)行時(shí)都會(huì)注意到這個(gè)變量是共享的,因此不會(huì)將該變量上的操作與其他內(nèi)存操作一起重排序。volatile變量不會(huì)被緩存在寄存器或者對(duì)其他處理器不可見的地方,因此在讀取volatile類型的變量時(shí)總會(huì)返回最新寫入的值。
一、volatile的內(nèi)存語義是:
當(dāng)寫一個(gè)volatile變量時(shí),JMM會(huì)把該線程對(duì)應(yīng)的本地內(nèi)存中的共享變量值立即刷新到主內(nèi)存中。
當(dāng)讀一個(gè)volatile變量時(shí),JMM會(huì)把該線程對(duì)應(yīng)的本地內(nèi)存設(shè)置為無效,直接從主內(nèi)存中讀取共享變量。
二、volatile底層的實(shí)現(xiàn)機(jī)制
如果把加入volatile關(guān)鍵字的代碼和未加入volatile關(guān)鍵字的代碼都生成匯編代碼,會(huì)發(fā)現(xiàn)加入volatile關(guān)鍵字的代碼會(huì)多出一個(gè)lock前綴指令。
1 、重排序時(shí)不能把后面的指令重排序到內(nèi)存屏障之前的位置
2、使得本CPU的Cache寫入內(nèi)存
3、寫入動(dòng)作也會(huì)引起別的CPU或者別的內(nèi)核無效化其Cache,相當(dāng)于讓新寫入的值對(duì)別的線程可見。
233.請(qǐng)寫出常用的Java多線程啟動(dòng)方式,Executors線程池有幾種常用類型?
(1) 繼承Thread類

(2) 實(shí)現(xiàn)Runnable接口

在Executor框架下,利用Executors的靜態(tài)方法可以創(chuàng)建三種類型的常用線程池:
1)FixedThreadPool這個(gè)線程池可以創(chuàng)建固定線程數(shù)的線程池。
2)SingleThreadExecutor是使用單個(gè)worker線程的Executor。
3)CachedThreadPool是一個(gè)”無限“容量的線程池,它會(huì)根據(jù)需要?jiǎng)?chuàng)建新線程。
234.關(guān)于sleep()和wait(),以下描述錯(cuò)誤的一項(xiàng)是()
A.sleep是線程類(Thread)的方法,wait是Object類的方法
B.Sleep不釋放對(duì)象鎖,wait放棄對(duì)象鎖
C.Sleep暫停線程、但監(jiān)控狀態(tài)任然保持,結(jié)束后會(huì)自動(dòng)恢復(fù)
D.Wait后進(jìn)入等待鎖定池,只針對(duì)此對(duì)象發(fā)出notify方法后獲取對(duì)象鎖進(jìn)入運(yùn)行狀態(tài)。
答案:D
分析:針對(duì)此對(duì)象的notify方法后獲取對(duì)象鎖并進(jìn)入就緒狀態(tài),而不是運(yùn)行狀態(tài)。另外針對(duì)此對(duì)象的notifyAll方法后也可能獲取對(duì)象鎖并進(jìn)入就緒狀態(tài),而不是運(yùn)行狀態(tài)
235.進(jìn)程和線程的區(qū)別是什么?
進(jìn)程是具有一定獨(dú)立功能的程序關(guān)于某個(gè)數(shù)據(jù)集合上的一次運(yùn)行活動(dòng),進(jìn)程是系統(tǒng)進(jìn)行資源分配和調(diào)度的一個(gè)獨(dú)立單位.
線程是進(jìn)程的一個(gè)實(shí)體,是CPU調(diào)度和分派的基本單位,它是比進(jìn)程更小的能獨(dú)立運(yùn)行的基本單位.線程自己基本上不擁有系統(tǒng)資源,只擁有一點(diǎn)在運(yùn)行中必不可少的資源(如程序計(jì)數(shù)器,一組寄存器和棧),但是它可與同屬一個(gè)進(jìn)程的其他的線程共享進(jìn)程所擁有的全部資源.
區(qū)別進(jìn)程線程根本區(qū)別?作為資源分配的單位?調(diào)度和執(zhí)行的單位開銷?每個(gè)進(jìn)程都有獨(dú)立的代碼和數(shù)據(jù)空間(進(jìn)程上下文),進(jìn)程間的切換會(huì)有較大的開銷。?線程可以看成時(shí)輕量級(jí)的進(jìn)程,同一類線程共享代碼和數(shù)據(jù)空間,每個(gè)線程有獨(dú)立的運(yùn)行棧和程序計(jì)數(shù)器(PC),線程切換的開銷小。?所處環(huán)境?系統(tǒng)在運(yùn)行的時(shí)候會(huì)為每個(gè)進(jìn)程分配不同的內(nèi)存區(qū)域?除了CPU之外,不會(huì)為線程分配內(nèi)存(線程所使用的資源是它所屬的進(jìn)程的資源),線程組只能共享資源?分配內(nèi)存?系統(tǒng)在運(yùn)行的時(shí)候會(huì)為每個(gè)進(jìn)程分配不同的內(nèi)存區(qū)域?除了CPU之外,不會(huì)為線程分配內(nèi)存(線程所使用的資源是它所屬的進(jìn)程的資源),線程組只能共享資源?包含關(guān)系?沒有線程的進(jìn)程是可以被看作單線程的,如果一個(gè)進(jìn)程內(nèi)擁有多個(gè)線程,則執(zhí)行過程不是一條線的,而是多條線(線程)共同完成的。?線程是進(jìn)程的一部分,所以線程有的時(shí)候被稱為是輕權(quán)進(jìn)程或者輕量級(jí)進(jìn)程。?
236.以下鎖機(jī)機(jī)制中,不能保證線程安全的是()
A.Lock
B.Synchronized
C.Volatile
答案:C
237.創(chuàng)建n多個(gè)線程,如何保證這些線程同時(shí)啟動(dòng)?看清,是“同時(shí)”。
答:用一個(gè)for循環(huán)創(chuàng)建線程對(duì)象,同時(shí)調(diào)用wait()方法,讓所有線程等待;直到最后一個(gè)線程也準(zhǔn)備就緒后,調(diào)用notifyAll(), 同時(shí)啟動(dòng)所有線程。
比如:給你n個(gè)賽車,讓他們都在起跑線上就緒后,同時(shí)出發(fā),Java多線程如何寫代碼?
思路是,來一輛賽車就加上一把鎖,并修改對(duì)應(yīng)的操作數(shù),如果沒有全部就緒就等待,并釋放鎖,直到最后一輛賽車到場(chǎng)后喚醒所有的賽車線程。代碼參考如下:


238.同步和異步有何異同,在什么情況下分別使用它們?
答:1.如果數(shù)據(jù)將在線程間共享。例如正在寫的數(shù)據(jù)以后可能被另一個(gè)線程讀到,或者正在讀的數(shù)據(jù)可能已經(jīng)被另一個(gè)線程寫過了,那么這些數(shù)據(jù)就是共享數(shù)據(jù),必須進(jìn)行同步存取。
2.當(dāng)應(yīng)用程序在對(duì)象上調(diào)用了一個(gè)需要花費(fèi)很長(zhǎng)時(shí)間來執(zhí)行的方法,并且不希望讓程序等待方法的返回時(shí),就應(yīng)該使用異步編程,在很多情況下采用異步途徑往往更有效率。
3.舉個(gè)例子: 打電話是同步 發(fā)消息是異步
239.Java線程中,sleep()和wait()區(qū)別
答:sleep是線程類(Thread)的方法;作用是導(dǎo)致此線程暫停執(zhí)行指定時(shí)間,給執(zhí)行機(jī)會(huì)給其他線程,但是監(jiān)控狀態(tài)依然保持,到時(shí)后會(huì)自動(dòng)恢復(fù);調(diào)用sleep()不會(huì)釋放對(duì)象鎖。
wait是Object類的方法;對(duì)此對(duì)象調(diào)用wait方法導(dǎo)致本線程放棄對(duì)象鎖,進(jìn)入等 待此對(duì)象的等待鎖定池。只有針對(duì)此對(duì)象發(fā)出notify方法(或notifyAll)后本線程才進(jìn)入對(duì)象鎖定池,準(zhǔn)備獲得對(duì)象鎖進(jìn)行運(yùn)行狀態(tài)。
240.下面所述步驟中,是創(chuàng)建進(jìn)程做必須的步驟是()
A.由調(diào)度程序?yàn)檫M(jìn)程分配CPUB.建立一個(gè)進(jìn)程控制塊C.為進(jìn)程分配內(nèi)存D.為進(jìn)程分配文件描述符
答案:BC
241.無鎖化編程有哪些常見方法?()
A.針對(duì)計(jì)數(shù)器,可以使用原子加
B.只有一個(gè)生產(chǎn)者和一個(gè)消費(fèi)者,那么就可以做到免鎖訪問環(huán)形緩沖區(qū)(Ring Buffer)
C.RCU(Read-Copy-Update),新舊副本切換機(jī)制,對(duì)于舊副本可以采用延遲釋放的做法
D.CAS(Compare-and-Swap),如無鎖棧,無鎖隊(duì)列等待
答案:D
分析:A 這方法雖然不太好,但是常見
B ProducerConsumerQueue就是這個(gè),到處都是
C linux kernel里面大量使用
D 本質(zhì)上其實(shí)就是樂觀鎖,操作起來很困難。。單生產(chǎn)者多消費(fèi)者或者多生產(chǎn)者單消費(fèi)者的情況下比較常見,也不容易遇到ABA問題。
242.sleep()和yield()有什么區(qū)別?
答:① sleep()方法給其他線程運(yùn)行機(jī)會(huì)時(shí)不考慮線程的優(yōu)先級(jí),因此會(huì)給低優(yōu)先級(jí)的線程以運(yùn)行的機(jī)會(huì);yield()方法只會(huì)給相同優(yōu)先級(jí)或更高優(yōu)先級(jí)的線程以運(yùn)行的機(jī)會(huì);
② 線程執(zhí)行sleep()方法后轉(zhuǎn)入阻塞(blocked)狀態(tài),而執(zhí)行yield()方法后轉(zhuǎn)入就緒(ready)狀態(tài);
③ sleep()方法聲明拋出InterruptedException,而yield()方法沒有聲明任何異常;
④ sleep()方法比yield()方法(跟操作系統(tǒng)相關(guān))具有更好的可移植性。?
243.當(dāng)一個(gè)線程進(jìn)入一個(gè)對(duì)象的synchronized方法A之后,其它線程是否可進(jìn)入此對(duì)象的synchronized方法?
答:不能。其它線程只能訪問該對(duì)象的非同步方法,同步方法則不能進(jìn)入。?只有等待當(dāng)前線程執(zhí)行完畢釋放鎖資源之后,其他線程才有可能進(jìn)行執(zhí)行該同步方法!
延伸 對(duì)象鎖分為三種:共享資源、this、當(dāng)前類的字節(jié)碼文件對(duì)象
244.請(qǐng)說出與線程同步相關(guān)的方法。
答:1. wait():使一個(gè)線程處于等待(阻塞)狀態(tài),并且釋放所持有的對(duì)象的鎖;
2. sleep():使一個(gè)正在運(yùn)行的線程處于睡眠狀態(tài),是一個(gè)靜態(tài)方法,調(diào)用此方法要捕捉InterruptedException 異常;
3. notify():喚醒一個(gè)處于等待狀態(tài)的線程,當(dāng)然在調(diào)用此方法的時(shí)候,并不能確切的喚醒某一個(gè)等待狀態(tài)的線程,而是由JVM確定喚醒哪個(gè)線程,而且與優(yōu)先級(jí)無關(guān);
4. notityAll():喚醒所有處入等待狀態(tài)的線程,注意并不是給所有喚醒線程一個(gè)對(duì)象的鎖,而是讓它們競(jìng)爭(zhēng);
5. JDK 1.5通過Lock接口提供了顯式(explicit)的鎖機(jī)制,增強(qiáng)了靈活性以及對(duì)線程的協(xié)調(diào)。Lock接口中定義了加鎖(lock())和解鎖(unlock())的方法,同時(shí)還提供了newCondition()方法來產(chǎn)生用于線程之間通信的Condition對(duì)象;
6. JDK 1.5還提供了信號(hào)量(semaphore)機(jī)制,信號(hào)量可以用來限制對(duì)某個(gè)共享資源進(jìn)行訪問的線程的數(shù)量。在對(duì)資源進(jìn)行訪問之前,線程必須得到信號(hào)量的許可(調(diào)用Semaphore對(duì)象的acquire()方法);在完成對(duì)資源的訪問后,線程必須向信號(hào)量歸還許可(調(diào)用Semaphore對(duì)象的release()方法)。
下面的例子演示了100個(gè)線程同時(shí)向一個(gè)銀行賬戶中存入1元錢,在沒有使用同步機(jī)制和使用同步機(jī)制情況下的執(zhí)行情況。
銀行賬戶類:

存錢線程類:

測(cè)試類:

在沒有同步的情況下,執(zhí)行結(jié)果通常是顯示賬戶余額在10元以下,出現(xiàn)這種狀況的原因是,當(dāng)一個(gè)線程A試圖存入1元的時(shí)候,另外一個(gè)線程B也能夠進(jìn)入存款的方法中,線程B讀取到的賬戶余額仍然是線程A存入1元錢之前的賬戶余額,因此也是在原來的余額0上面做了加1元的操作,同理線程C也會(huì)做類似的事情,所以最后100個(gè)線程執(zhí)行結(jié)束時(shí),本來期望賬戶余額為100元,但實(shí)際得到的通常在10元以下。解決這個(gè)問題的辦法就是同步,當(dāng)一個(gè)線程對(duì)銀行賬戶存錢時(shí),需要將此賬戶鎖定,待其操作完成后才允許其他的線程進(jìn)行操作,代碼有如下幾種調(diào)整方案:
在銀行賬戶的存款(deposit)方法上同步(synchronized)關(guān)鍵字

2. 在線程調(diào)用存款方法時(shí)對(duì)銀行賬戶進(jìn)行同步

3. 通過JDK 1.5顯示的鎖機(jī)制,為每個(gè)銀行賬戶創(chuàng)建一個(gè)鎖對(duì)象,在存款操作進(jìn)行加鎖和解鎖的操作

按照上述三種方式對(duì)代碼進(jìn)行修改后,重寫執(zhí)行測(cè)試代碼Test01,將看到最終的賬戶余額為100元。?
245.編寫多線程程序有幾種實(shí)現(xiàn)方式?
答:Java 5以前實(shí)現(xiàn)多線程有兩種實(shí)現(xiàn)方法:一種是繼承Thread類;另一種是實(shí)現(xiàn)Runnable接口。兩種方式都要通過重寫run()方法來定義線程的行為,推薦使用后者,因?yàn)镴ava中的繼承是單繼承,一個(gè)類有一個(gè)父類,如果繼承了Thread類就無法再繼承其他類了,同時(shí)也可以實(shí)現(xiàn)資源共享,顯然使用Runnable接口更為靈活。
補(bǔ)充:Java 5以后創(chuàng)建線程還有第三種方式:實(shí)現(xiàn)Callable接口,該接口中的call方法可以在線程執(zhí)行結(jié)束時(shí)產(chǎn)生一個(gè)返回值,代碼如下所示:

246.synchronized關(guān)鍵字的用法?
答:synchronized關(guān)鍵字可以將對(duì)象或者方法標(biāo)記為同步,以實(shí)現(xiàn)對(duì)對(duì)象和方法的互斥訪問,可以用synchronized(對(duì)象) { … }定義同步代碼塊,或者在聲明方法時(shí)將synchronized作為方法的修飾符。在第60題的例子中已經(jīng)展示了synchronized關(guān)鍵字的用法。?
247.啟動(dòng)一個(gè)線程是用run()還是start()方法?
答:?jiǎn)?dòng)一個(gè)線程是調(diào)用start()方法,使線程所代表的虛擬處理機(jī)處于可運(yùn)行狀態(tài),這意味著它可以由JVM 調(diào)度并執(zhí)行,這并不意味著線程就會(huì)立即運(yùn)行。run()方法是線程啟動(dòng)后要進(jìn)行回調(diào)(callback)的方法。?
API解釋如下:

248.什么是線程池(thread pool)?
答:在面向?qū)ο缶幊讨?,?chuàng)建和銷毀對(duì)象是很費(fèi)時(shí)間的,因?yàn)閯?chuàng)建一個(gè)對(duì)象要獲取內(nèi)存資源或者其它更多資源。在Java中更是如此,虛擬機(jī)將試圖跟蹤每一個(gè)對(duì)象,以便能夠在對(duì)象銷毀后進(jìn)行垃圾回收。所以提高服務(wù)程序效率的一個(gè)手段就是盡可能減少創(chuàng)建和銷毀對(duì)象的次數(shù),特別是一些很耗資源的對(duì)象創(chuàng)建和銷毀,這就是"池化資源"技術(shù)產(chǎn)生的原因。線程池顧名思義就是事先創(chuàng)建若干個(gè)可執(zhí)行的線程放入一個(gè)池(容器)中,需要的時(shí)候從池中獲取線程不用自行創(chuàng)建,使用完畢不需要銷毀線程而是放回池中,從而減少創(chuàng)建和銷毀線程對(duì)象的開銷。
Java 5+中的Executor接口定義一個(gè)執(zhí)行線程的工具。它的子類型即線程池接口是ExecutorService。要配置一個(gè)線程池是比較復(fù)雜的,尤其是對(duì)于線程池的原理不是很清楚的情況下,因此在工具類Executors面提供了一些靜態(tài)工廠方法,生成一些常用的線程池,如下所示:
newSingleThreadExecutor:創(chuàng)建一個(gè)單線程的線程池。這個(gè)線程池只有一個(gè)線程在工作,也就是相當(dāng)于單線程串行執(zhí)行所有任務(wù)。如果這個(gè)唯一的線程因?yàn)楫惓=Y(jié)束,那么會(huì)有一個(gè)新的線程來替代它。此線程池保證所有任務(wù)的執(zhí)行順序按照任務(wù)的提交順序執(zhí)行。
newFixedThreadPool:創(chuàng)建固定大小的線程池。每次提交一個(gè)任務(wù)就創(chuàng)建一個(gè)線程,直到線程達(dá)到線程池的最大大小。線程池的大小一旦達(dá)到最大值就會(huì)保持不變,如果某個(gè)線程因?yàn)閳?zhí)行異常而結(jié)束,那么線程池會(huì)補(bǔ)充一個(gè)新線程。
newCachedThreadPool:創(chuàng)建一個(gè)可緩存的線程池。如果線程池的大小超過了處理任務(wù)所需要的線程,那么就會(huì)回收部分空閑(60秒不執(zhí)行任務(wù))的線程,當(dāng)任務(wù)數(shù)增加時(shí),此線程池又可以智能的添加新線程來處理任務(wù)。此線程池不會(huì)對(duì)線程池大小做限制,線程池大小完全依賴于操作系統(tǒng)(或者說JVM)能夠創(chuàng)建的最大線程大小。
newScheduledThreadPool:創(chuàng)建一個(gè)大小無限的線程池。此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)的需求。
newSingleThreadExecutor:創(chuàng)建一個(gè)單線程的線程池。此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)的需求。
有通過Executors工具類創(chuàng)建線程池并使用線程池執(zhí)行線程的代碼。如果希望在服務(wù)器上使用線程池,強(qiáng)烈建議使用newFixedThreadPool方法來創(chuàng)建線程池,這樣能獲得更好的性能。?
249.線程的基本狀態(tài)以及狀態(tài)之間的關(guān)系?

除去起始(new)狀態(tài)和結(jié)束(finished)狀態(tài),線程有三種狀態(tài),分別是:就緒(ready)、運(yùn)行(running)和阻塞(blocked)。其中就緒狀態(tài)代表線程具備了運(yùn)行的所有條件,只等待CPU調(diào)度(萬事俱備,只欠東風(fēng));處于運(yùn)行狀態(tài)的線程可能因?yàn)镃PU調(diào)度(時(shí)間片用完了)的原因回到就緒狀態(tài),也有可能因?yàn)檎{(diào)用了線程的yield方法回到就緒狀態(tài),此時(shí)線程不會(huì)釋放它占有的資源的鎖,坐等CPU以繼續(xù)執(zhí)行;運(yùn)行狀態(tài)的線程可能因?yàn)镮/O中斷、線程休眠、調(diào)用了對(duì)象的wait方法而進(jìn)入阻塞狀態(tài)(有的地方也稱之為等待狀態(tài));而進(jìn)入阻塞狀態(tài)的線程會(huì)因?yàn)樾菝呓Y(jié)束、調(diào)用了對(duì)象的notify方法或notifyAll方法或其他線程執(zhí)行結(jié)束而進(jìn)入就緒狀態(tài)。注意:調(diào)用wait方法會(huì)讓線程進(jìn)入等待池中等待被喚醒,notify方法或notifyAll方法會(huì)讓等待鎖中的線程從等待池進(jìn)入等鎖池,在沒有得到對(duì)象的鎖之前,線程仍然無法獲得CPU的調(diào)度和執(zhí)行。
250.簡(jiǎn)述synchronized 和java.util.concurrent.locks.Lock的異同?
答:Lock是Java 5以后引入的新的API,和關(guān)鍵字synchronized相比主要相同點(diǎn):Lock 能完成synchronized所實(shí)現(xiàn)的所有功能;主要不同點(diǎn):Lock 有比synchronized 更精確的線程語義和更好的性能。synchronized 會(huì)自動(dòng)釋放鎖,而Lock 一定要求程序員手工釋放,并且必須在finally 塊中釋放(這是釋放外部資源的最好的地方)。?
251.創(chuàng)建線程的兩種方式分別是什么,優(yōu)缺點(diǎn)是什么?
方式1:繼承Java.lang.Thread類,并覆蓋run() 方法。
優(yōu)勢(shì):編寫簡(jiǎn)單;
劣勢(shì):?jiǎn)卫^承的限制----無法繼承其它父類,同時(shí)不能實(shí)現(xiàn)資源共享。

方式2:實(shí)現(xiàn)Java.lang.Runnable接口,并實(shí)現(xiàn)run()方法。
優(yōu)勢(shì):可繼承其它類,多線程可共享同一個(gè)Thread對(duì)象;
劣勢(shì):編程方式稍微復(fù)雜,如需訪問當(dāng)前線程,需調(diào)用Thread.currentThread()方法

252.Java創(chuàng)建線程后,調(diào)用start()方法和run()的區(qū)別
兩種方法的區(qū)別
1) start方法:
用start方法來啟動(dòng)線程,真正實(shí)現(xiàn)了多線程運(yùn)行,這時(shí)無需等待run方法體代碼執(zhí)行完畢而直接繼續(xù)執(zhí)行下面的代碼。通過調(diào)用Thread類的start()方法來啟動(dòng)一個(gè)線程,這時(shí)此線程處于就緒(可運(yùn)行)狀態(tài),并沒有運(yùn)行,一旦得到cpu時(shí)間片,就開始執(zhí)行run()方法,這里方法run()稱為線程體,它包含了要執(zhí)行的這個(gè)線程的內(nèi)容,Run方法運(yùn)行結(jié)束,此線程隨即終止。
2) run():
run()方法只是類的一個(gè)普通方法而已,如果直接調(diào)用run方法,程序中依然只有主線程這一個(gè)線程,其程序執(zhí)行路徑還是只有一條,還是要順序執(zhí)行,還是要等待,run方法體執(zhí)行完畢后才可繼續(xù)執(zhí)行下面的代碼,這樣就沒有達(dá)到寫線程的目的。
總結(jié):調(diào)用start方法方可啟動(dòng)線程,而run方法只是thread的一個(gè)普通方法調(diào)用,還是在主線程里執(zhí)行。這兩個(gè)方法應(yīng)該都比較熟悉,把需要并行處理的代碼放在run()方法中,start()方法啟動(dòng)線程將自動(dòng)調(diào)用 run()方法,這是由jvm的內(nèi)存機(jī)制規(guī)定的。并且run()方法必須是public訪問權(quán)限,返回值類型為void。
兩種方式的比較 :
實(shí)際中往往采用實(shí)現(xiàn)Runable接口,一方面因?yàn)閖ava只支持單繼承,繼承了Thread類就無法再繼續(xù)繼承其它類,而且Runable接口只有一個(gè)run方法;另一方面通過結(jié)果可以看出實(shí)現(xiàn)Runable接口才是真正的多線程。
253.線程的生命周期
線程是一個(gè)動(dòng)態(tài)執(zhí)行的過程,它也有一個(gè)從產(chǎn)生到死亡的過程。
生命周期的五種狀態(tài)
新建(new Thread)
當(dāng)創(chuàng)建Thread類的一個(gè)實(shí)例(對(duì)象)時(shí),此線程進(jìn)入新建狀態(tài)(未被啟動(dòng))
例如:Thread t1=new Thread();
就緒(runnable)
線程已經(jīng)被啟動(dòng),正在等待被分配給CPU時(shí)間片,也就是說此時(shí)線程正在就緒隊(duì)列中排隊(duì)等候得到CPU資源。例如:t1.start();
運(yùn)行(running)
線程獲得CPU資源正在執(zhí)行任務(wù)(run()方法),此時(shí)除非此線程自動(dòng)放棄CPU資源或者有優(yōu)先級(jí)更高的線程進(jìn)入,線程將一直運(yùn)行到結(jié)束。
死亡(dead)
當(dāng)線程執(zhí)行完畢或被其它線程殺死,線程就進(jìn)入死亡狀態(tài),這時(shí)線程不可能再進(jìn)入就緒狀態(tài)等待執(zhí)行。
自然終止:正常運(yùn)行run()方法后終止
異常終止:調(diào)用stop()方法讓一個(gè)線程終止運(yùn)行
堵塞(blocked)
由于某種原因?qū)е抡谶\(yùn)行的線程讓出CPU并暫停自己的執(zhí)行,即進(jìn)入堵塞狀態(tài)。
正在睡眠:用sleep(long t) 方法可使線程進(jìn)入睡眠方式。一個(gè)睡眠著的線程在指定的時(shí)間過去可進(jìn)入就緒狀態(tài)。
正在等待:調(diào)用wait()方法。(調(diào)用motify()方法回到就緒狀態(tài))
被另一個(gè)線程所阻塞:調(diào)用suspend()方法。(調(diào)用resume()方法恢復(fù))
254.如何實(shí)現(xiàn)線程同步?
當(dāng)多個(gè)線程訪問同一個(gè)數(shù)據(jù)時(shí),容易出現(xiàn)線程安全問題,需要某種方式來確保資源在某一時(shí)刻只被一個(gè)線程使用。需要讓線程同步,保證數(shù)據(jù)安全
線程同步的實(shí)現(xiàn)方案:
1)同步代碼塊,使用synchronized關(guān)鍵字
同步代碼塊:
synchronized (同步鎖) {
授課代碼;
}
同步方法:
public synchronized void makeWithdrawal(int amt) { }
線程同步的好處:解決了線程安全問題
線程同步的缺點(diǎn):性能下降,可能會(huì)帶來死鎖
注意: 同步代碼塊,所使用的同步鎖可以是三種,
1、this 2、 共享資源 3、 字節(jié)碼文件對(duì)象
同步方法所使用的同步鎖,默認(rèn)的是this
255.說說關(guān)于同步鎖的更多細(xì)節(jié)
答:Java中每個(gè)對(duì)象都有一個(gè)內(nèi)置鎖。
當(dāng)程序運(yùn)行到非靜態(tài)的synchronized同步方法上時(shí),自動(dòng)獲得與正在執(zhí)行代碼類的當(dāng)前實(shí)例(this實(shí)例)有關(guān)的鎖。獲得一個(gè)對(duì)象的鎖也稱為獲取鎖、鎖定對(duì)象、在對(duì)象上鎖定或在對(duì)象上同步。
當(dāng)程序運(yùn)行到synchronized同步方法或代碼塊時(shí)才該對(duì)象鎖才起作用。
一個(gè)對(duì)象只有一個(gè)鎖。所以,如果一個(gè)線程獲得該鎖,就沒有其他線程可以獲得鎖,直到第一個(gè)線程釋放(或返回)鎖。這也意味著任何其他線程都不能進(jìn)入該對(duì)象上的synchronized方法或代碼塊,直到該鎖被釋放。
釋放鎖是指持鎖線程退出了synchronized同步方法或代碼塊。
關(guān)于鎖和同步,有一下幾個(gè)要點(diǎn):
1)只能同步方法,而不能同步變量和類;
2)每個(gè)對(duì)象只有一個(gè)鎖;當(dāng)提到同步時(shí),應(yīng)該清楚在什么上同步?也就是說,在哪個(gè)對(duì)象上同步?
3)不必同步類中所有的方法,類可以同時(shí)擁有同步和非同步方法。
4)如果兩個(gè)線程要執(zhí)行一個(gè)類中的synchronized方法,并且兩個(gè)線程使用相同的實(shí)例來調(diào)用方法,那么一次只能有一個(gè)線程能夠執(zhí)行方法,另一個(gè)需要等待,直到鎖被釋放。也就是說:如果一個(gè)線程在對(duì)象上獲得一個(gè)鎖,就沒有任何其他線程可以進(jìn)入(該對(duì)象的)類中的任何一個(gè)同步方法。
5)如果線程擁有同步和非同步方法,則非同步方法可以被多個(gè)線程自由訪問而不受鎖的限制。
6)線程睡眠時(shí),它所持的任何鎖都不會(huì)釋放。
7)線程可以獲得多個(gè)鎖。比如,在一個(gè)對(duì)象的同步方法里面調(diào)用另外一個(gè)對(duì)象的同步方法,則獲取了兩個(gè)對(duì)象的同步鎖。
8)同步損害并發(fā)性,應(yīng)該盡可能縮小同步范圍。同步不但可以同步整個(gè)方法,還可以同步方法中一部分代碼塊。
9)在使用同步代碼塊時(shí)候,應(yīng)該指定在哪個(gè)對(duì)象上同步,也就是說要獲取哪個(gè)對(duì)象的鎖。
256.Java中實(shí)現(xiàn)線程通信的三個(gè)方法的作用是什么?
Java提供了3個(gè)方法解決線程之間的通信問題,均是java.lang.Object類的方法,都只能在同步方法或者同步代碼塊中使用,否則會(huì)拋出異常。
方法名作 用final void wait()表示線程一直等待,直到其它線程通知void wait(long timeout)線程等待指定毫秒?yún)?shù)的時(shí)間final void wait(long timeout,int nanos)線程等待指定毫秒、微妙的時(shí)間final void notify()喚醒一個(gè)處于等待狀態(tài)的線程。注意的是在調(diào)用此方法的時(shí)候,并不能確切的喚醒某一個(gè)等待狀態(tài)的線程,而是由JVM確定喚醒哪個(gè)線程,而且不是按優(yōu)先級(jí)。final void notifyAll()喚醒同一個(gè)對(duì)象上所有調(diào)用wait()方法的線程,注意并不是給所有喚醒線程一個(gè)對(duì)象的鎖,而是讓它們競(jìng)爭(zhēng)
IO流:
257.下面哪個(gè)流類屬于面向字符的輸入流()選擇一項(xiàng))
A.BufferedWriter
B.FileInputStream
C.ObjectInputStream
D.InputStreamReader
答案:D
分析:A:字符輸出的緩沖流
B:字節(jié)輸入流
C:對(duì)象輸入流
258.要從文件”file.dat”文件中讀出第10個(gè)字節(jié)到變量c中,下列哪個(gè)正確()(選擇一項(xiàng))
A.FileInputStream in=new FileInputStream("file.dat");
in.skip(9);
int c=in.read();
B.FileInputStream in=new FileInputStream("file.dat");
in.skip(10);
int c=in.read();
C.FileInputStream in=new FileInputStream("file.dat");
int c=in.read();
D.RandomAccessFile in=new RandomAccessFile("file.dat");
in.skip(7);
int c=in.readByte();
答案:A
分析: skip(long n)該方法中的n指的是要跳過的字節(jié)數(shù)
259.新建一個(gè)流對(duì)象,下面那個(gè)選項(xiàng)的代碼是錯(cuò)誤的?()
A.new BufferedWriter(new FileWriter(“a.txt”));
B.new BufferedReader (new FileInputStream(“a.dat”));
C.new GZIPOutputStream(new FileOutputStream(“a.zip”));
D.new ObjectInputStream(new FileInputStream(“a.dat”));
答案:B
分析:BufferedReader類的參數(shù)只能是Reader類型的,不能是InputStream類型。
260.下面哪個(gè)流是面向字符的輸入流()
A.BufferedWriter
B.FileInputStream
C.ObjectInputStream
D.InputStreamReader
答案:D
以InputStream(輸入流)/OutputStream(輸出流)為后綴的是字節(jié)流;
以Reader(輸入流)/Writer(輸出流)為后綴的是字符流。
261.Java類庫(kù)中,將信息寫入內(nèi)存的類是()
A.Java.io.FileOutputStream
B.java.ByteArrayOutputStream
C.java.io.BufferedOutputStream
D.java,.io.DataOutputStream
答案:B
分析: ACD都是io到文件
262.請(qǐng)寫出一段代碼,能夠完成將字符串寫入文件

263.下面哪個(gè)流類屬于面向字符的輸入流()
A.BufferedWriter
B.FileInputStream
C.ObjectInputStream
D.InputStreamReader
答案:D
264.Java中如何實(shí)現(xiàn)序列化,有什么意義?
答:序列化就是一種用來處理對(duì)象流的機(jī)制,所謂對(duì)象流也就是將對(duì)象的內(nèi)容進(jìn)行流化??梢詫?duì)流化后的對(duì)象進(jìn)行讀寫操作,也可將流化后的對(duì)象傳輸于網(wǎng)絡(luò)之間。序列化是為了解決對(duì)象流讀寫操作時(shí)可能引發(fā)的問題(如果不進(jìn)行序列化可能會(huì)存在數(shù)據(jù)亂序的問題)。
要實(shí)現(xiàn)序列化,需要讓一個(gè)類實(shí)現(xiàn)Serializable接口,該接口是一個(gè)標(biāo)識(shí)性接口,標(biāo)注該類對(duì)象是可被序列化的,然后使用一個(gè)輸出流來構(gòu)造一個(gè)對(duì)象輸出流并通過writeObject(Object obj)方法就可以將實(shí)現(xiàn)對(duì)象寫出(即保存其狀態(tài));如果需要反序列化則可以用一個(gè)輸入流建立對(duì)象輸入流,然后通過readObject方法從流中讀取對(duì)象。序列化除了能夠?qū)崿F(xiàn)對(duì)象的持久化之外,還能夠用于對(duì)象的深度克?。▍⒁奐ava面試題集1-29題)?
265.Java 中有幾種類型的流?
答:兩種流分別是字節(jié)流,字符流。
字節(jié)流繼承于InputStream、OutputStream,字符流繼承于Reader、Writer。在java.io 包中還有許多其他的流,主要是為了提高性能和使用方便。
補(bǔ)充:關(guān)于Java的IO需要注意的有兩點(diǎn):一是兩種對(duì)稱性(輸入和輸出的對(duì)稱性,字節(jié)和字符的對(duì)稱性);二是兩種設(shè)計(jì)模式(適配器模式和裝潢模式)。另外Java中的流不同于C#的是它只有一個(gè)維度一個(gè)方向。
補(bǔ)充:下面用IO和NIO兩種方式實(shí)現(xiàn)文件拷貝,這個(gè)題目在面試的時(shí)候是經(jīng)常被問到的。

注意:上面用到Java 7的TWR,使用TWR后可以不用在finally中釋放外部資源 ,從而讓代碼更加優(yōu)雅。
266.寫一個(gè)方法,輸入一個(gè)文件名和一個(gè)字符串,統(tǒng)計(jì)這個(gè)字符串在這個(gè)文件中出現(xiàn)的次數(shù)。
答:代碼如下:

267.輸入流和輸出流聯(lián)系和區(qū)別,節(jié)點(diǎn)流和處理流聯(lián)系和區(qū)別
首先,你要明白什么是“流”。直觀地講,流就像管道一樣,在程序和文件之間,輸入輸出的方向是針對(duì)程序而言,向程序中讀入東西,就是輸入流,從程序中向外讀東西,就是輸出流。
輸入流是得到數(shù)據(jù),輸出流是輸出數(shù)據(jù),而節(jié)點(diǎn)流,處理流是流的另一種劃分,按照功能不同進(jìn)行的劃分。節(jié)點(diǎn)流,可以從或向一個(gè)特定的地方(節(jié)點(diǎn))讀寫數(shù)據(jù)。處理流是對(duì)一個(gè)已存在的流的連接和封裝,通過所封裝的流的功能調(diào)用實(shí)現(xiàn)數(shù)據(jù)讀寫。如BufferedReader。處理流的構(gòu)造方法總是要帶一個(gè)其他的流對(duì)象做參數(shù)。一個(gè)流對(duì)象經(jīng)過其他流的多次包裝,稱為流的鏈接。
268.字符流字節(jié)流聯(lián)系區(qū)別;什么時(shí)候使用字節(jié)流和字符流?
字符流和字節(jié)流是流的一種劃分,按處理照流的數(shù)據(jù)單位進(jìn)行的劃分。兩類都分為輸入和輸出操作。在字節(jié)流中輸出數(shù)據(jù)主要是使用OutputStream完成,輸入使的是InputStream,在字符流中輸出主要是使用Writer類完成,輸入流主要使用Reader類完成。這四個(gè)都是抽象類。
字符流處理的單元為2個(gè)字節(jié)的Unicode字符,分別操作字符、字符數(shù)組或字符串,而字節(jié)流處理單元為1個(gè)字節(jié),操作字節(jié)和字節(jié)數(shù)組。字節(jié)流是最基本的,所有的InputStrem和OutputStream的子類都是,主要用在處理二進(jìn)制數(shù)據(jù),它是按字節(jié)來處理的 但實(shí)際中很多的數(shù)據(jù)是文本,又提出了字符流的概念,它是按虛擬機(jī)的編碼來處理,也就是要進(jìn)行字符集的轉(zhuǎn)化 這兩個(gè)之間通過 InputStreamReader,OutputStreamWriter來關(guān)聯(lián),實(shí)際上是通過byte[]和String來關(guān)聯(lián)的。
269.列舉常用字節(jié)輸入流和輸出流并說明其特點(diǎn),至少5對(duì)。
FileInputStream 從文件系統(tǒng)中的某個(gè)文件中獲得輸入字節(jié)。
FileOutputStream 從程序當(dāng)中的數(shù)據(jù),寫入到指定文件。
ObjectInputStream 對(duì)以前使用 ObjectOutputStream 寫入的基本數(shù)據(jù)和對(duì)象進(jìn)行反序列化。 ObjectOutputStream 和ObjectInputStream 分別與FileOutputStream 和 FileInputStream 一起使用時(shí),可以為應(yīng)用程序提供對(duì)對(duì)象圖形的持久存儲(chǔ)。ObjectInputStream 用于恢復(fù)那些以前序列化的對(duì)象。其他用途包括使用套接字流在主機(jī)之間傳遞對(duì)象,或者用于編組和解組遠(yuǎn)程通信系統(tǒng)中的實(shí)參和形參。
ByteArrayInputStream 包含一個(gè)內(nèi)部緩沖區(qū),該緩沖區(qū)包含從流中讀取的字節(jié)。內(nèi)部計(jì)數(shù)器跟蹤 read 方法要提供的下一個(gè)字節(jié)。
FilterInputStream 包含其他一些輸入流,它將這些流用作其基本數(shù)據(jù)源,它可以直接傳輸數(shù)據(jù)或提供一些額外的功能。FilterInputStream 類本身只是簡(jiǎn)單地重寫那些將所有請(qǐng)求傳遞給所包含輸入流的 InputStream 的所有方法。FilterInputStream 的子類可進(jìn)一步重寫這些方法中的一些方法,并且還可以提供一些額外的方法和字段。
StringBufferInputStream此類允許應(yīng)用程序創(chuàng)建輸入流,在該流中讀取的字節(jié)由字符串內(nèi)容提供。應(yīng)用程序還可以使用ByteArrayInputStream 從 byte 數(shù)組中讀取字節(jié)。 只有字符串中每個(gè)字符的低八位可以由此類使用。
ByteArrayOutputStream此類實(shí)現(xiàn)了一個(gè)輸出流,其中的數(shù)據(jù)被寫入一個(gè) byte 數(shù)組。緩沖區(qū)會(huì)隨著數(shù)據(jù)的不斷寫入而自動(dòng)增長(zhǎng)??墒褂?toByteArray() 和 toString() 獲取數(shù)據(jù)。
FileOutputStream文件輸出流是用于將數(shù)據(jù)寫入 File 或FileDescriptor 的輸出流。文件是否可用或能否可以被創(chuàng)建取決于基礎(chǔ)平臺(tái)。特別是某些平臺(tái)一次只允許一個(gè) FileOutputStream(或其他文件寫入對(duì)象)打開文件進(jìn)行寫入。在這種情況下,如果所涉及的文件已經(jīng)打開,則此類中的構(gòu)造方法將失敗。
FilterOutputStream類是過濾輸出流的所有類的超類。這些流位于已存在的輸出流(基礎(chǔ) 輸出流)之上,它們將已存在的輸出流作為其基本數(shù)據(jù)接收器,但可能直接傳輸數(shù)據(jù)或提供一些額外的功能。 FilterOutputStream 類本身只是簡(jiǎn)單地重寫那些將所有請(qǐng)求傳遞給所包含輸出流的 OutputStream 的所有方法。FilterOutputStream 的子類可進(jìn)一步地重寫這些方法中的一些方法,并且還可以提供一些額外的方法和字段。
ObjectOutputStream 將 Java 對(duì)象的基本數(shù)據(jù)類型和圖形寫入 OutputStream??梢允褂?ObjectInputStream 讀?。ㄖ貥?gòu))對(duì)象。通過在流中使用文件可以實(shí)現(xiàn)對(duì)象的持久存儲(chǔ)。如果流是網(wǎng)絡(luò)套接字流,則可以在另一臺(tái)主機(jī)上或另一個(gè)進(jìn)程中重構(gòu)對(duì)象。
PipedOutputStream可以將管道輸出流連接到管道輸入流來創(chuàng)建通信管道。管道輸出流是管道的發(fā)送端。通常,數(shù)據(jù)由某個(gè)線程寫入 PipedOutputStream 對(duì)象,并由其他線程從連接的 PipedInputStream 讀取。不建議對(duì)這兩個(gè)對(duì)象嘗試使用單個(gè)線程,因?yàn)檫@樣可能會(huì)造成該線程死鎖。如果某個(gè)線程正從連接的管道輸入流中讀取數(shù)據(jù)字節(jié),但該線程不再處于活動(dòng)狀態(tài),則該管道被視為處于毀壞狀態(tài)。
270.說明緩沖流的優(yōu)點(diǎn)和原理
不帶緩沖的流的工作原理:
它讀取到一個(gè)字節(jié)/字符,就向用戶指定的路徑寫出去,讀一個(gè)寫一個(gè),所以就慢了。
帶緩沖的流的工作原理:
讀取到一個(gè)字節(jié)/字符,先不輸出,等湊足了緩沖的最大容量后一次性寫出去,從而提高了工作效率
優(yōu)點(diǎn):減少對(duì)硬盤的讀取次數(shù),降低對(duì)硬盤的損耗。
271.序列化的定義、實(shí)現(xiàn)和注意事項(xiàng)
想把一個(gè)對(duì)象寫在硬盤上或者網(wǎng)絡(luò)上,對(duì)其進(jìn)行序列化,把他序列化成為一個(gè)字節(jié)流。
實(shí)現(xiàn)和注意事項(xiàng):
1)實(shí)現(xiàn)接口Serializable Serializable接口中沒有任何的方法,實(shí)現(xiàn)該接口的類不需要實(shí)現(xiàn)額外的方法。
2)如果對(duì)象中的某個(gè)屬性是對(duì)象類型,必須也實(shí)現(xiàn)Serializable接口才可以,序列化對(duì)靜態(tài)變量無效
3)如果不希望某個(gè)屬性參與序列化,不是將其static,而是transient串行化保存的只是變量的值,對(duì)于變量的任何修飾符,都不能保存序列化版本不兼容
272.使用IO流完成文件夾復(fù)制
(結(jié)合遞歸)


273.說說BIO、NIO和AIO的區(qū)別
Java BIO: 同步并阻塞,服務(wù)器實(shí)現(xiàn)模式為一個(gè)連接一個(gè)線程,即客戶端有連接請(qǐng)求時(shí)服務(wù)器端就需要啟動(dòng)一個(gè)線程進(jìn)行處理,如果這個(gè)連接不做任何事情會(huì)造成不必要的線程開銷,當(dāng)然可以通過線程池機(jī)制改善。
Java NIO: 同步非阻塞,服務(wù)器實(shí)現(xiàn)模式為一個(gè)請(qǐng)求一個(gè)線程,即客戶端發(fā)送的連接請(qǐng)求都會(huì)注冊(cè)到多路復(fù)用器上,多路復(fù)用器輪詢到連接有I/O請(qǐng)求時(shí)才啟動(dòng)一個(gè)線程進(jìn)行處理。
Java AIO: 異步非阻塞,服務(wù)器實(shí)現(xiàn)模式為一個(gè)有效請(qǐng)求一個(gè)線程,客戶端的I/O請(qǐng)求都是由OS先完成了再通知服務(wù)器應(yīng)用去啟動(dòng)線程進(jìn)行處理。
NIO比BIO的改善之處是把一些無效的連接擋在了啟動(dòng)線程之前,減少了這部分資源的浪費(fèi)(因?yàn)槲覀兌贾烂縿?chuàng)建一個(gè)線程,就要為這個(gè)線程分配一定的內(nèi)存空間)
AIO比NIO的進(jìn)一步改善之處是將一些暫時(shí)可能無效的請(qǐng)求擋在了啟動(dòng)線程之前,比如在NIO的處理方式中,當(dāng)一個(gè)請(qǐng)求來的話,開啟線程進(jìn)行處理,但這個(gè)請(qǐng)求所需要的資源還沒有就緒,此時(shí)必須等待后端的應(yīng)用資源,這時(shí)線程就被阻塞了。
適用場(chǎng)景分析:
BIO方式適用于連接數(shù)目比較小且固定的架構(gòu),這種方式對(duì)服務(wù)器資源要求比較高,并發(fā)局限于應(yīng)用中,JDK1.4以前的唯一選擇,但程序直觀簡(jiǎn)單易理解,如之前在Apache中使用。
NIO方式適用于連接數(shù)目多且連接比較短(輕操作)的架構(gòu),比如聊天服務(wù)器,并發(fā)局限于應(yīng)用中,編程比較復(fù)雜,JDK1.4開始支持,如在 Nginx,Netty中使用。
AIO方式使用于連接數(shù)目多且連接比較長(zhǎng)(重操作)的架構(gòu),比如相冊(cè)服務(wù)器,充分調(diào)用OS參與并發(fā)操作,編程比較復(fù)雜,JDK7開始支持,在成長(zhǎng)中,Netty曾經(jīng)使用過,后來放棄。
網(wǎng)絡(luò)編程:
274.IP地址和端口號(hào)
1)IP地址
用來標(biāo)志網(wǎng)絡(luò)中的一個(gè)通信實(shí)體的地址。通信實(shí)體可以是計(jì)算機(jī),路由器等。
2)IP地址分類
IPV4:32位地址,以點(diǎn)分十進(jìn)制表示,如192.168.0.1
IPV6:128位(16個(gè)字節(jié))寫成8個(gè)16位的無符號(hào)整數(shù),每個(gè)整數(shù)用四個(gè)十六進(jìn)制位表示,數(shù)之間用冒號(hào)(:)分開,如:3ffe:3201:1401:1280:c8ff:fe4d:db39:1984
3)特殊的IP地址
127.0.0.1 本機(jī)地址
192.168.0.0--192.168.255.255私有地址,屬于非注冊(cè)地址,專門為組織機(jī)構(gòu)內(nèi)部使用。
4)端口:port
IP地址用來標(biāo)志一臺(tái)計(jì)算機(jī),但是一臺(tái)計(jì)算機(jī)上可能提供多種應(yīng)用程序,使用端口來區(qū)分這些應(yīng)用程序。 端口是虛擬的概念,并不是說在主機(jī)上真的有若干個(gè)端口。通過端口,可以在一個(gè)主機(jī)上運(yùn)行多個(gè)網(wǎng)絡(luò)應(yīng)用程序。 端口范圍0---65535,16位整數(shù)
5)端口分類
公認(rèn)端口 0—1023 比如80端口分配給WWW,21端口分配給FTP,22端口分配給SSH,23端口分配給telnet,25端口分配給smtp
注冊(cè)端口 1024—49151 分配給用戶進(jìn)程或應(yīng)用程序
動(dòng)態(tài)/私有端口 49152--65535
6)理解IP和端口的關(guān)系
IP地址好比每個(gè)人的地址(門牌號(hào)),端口好比是房間號(hào)。必須同時(shí)指定IP地址和端口號(hào)才能夠正確的發(fā)送數(shù)據(jù)
IP地址好比為電話號(hào)碼,而端口號(hào)就好比為分機(jī)號(hào)。
由于字?jǐn)?shù)限制,后續(xù)內(nèi)容更加精彩,歡迎關(guān)注,整理不易,可否動(dòng)動(dòng)你的小手給小編來點(diǎn)更新的動(dòng)力,希望對(duì)你們會(huì)有幫助!