一、IO和多線程專題
1.介紹下進(jìn)程和線程的關(guān)系
進(jìn)程:一個(gè)獨(dú)立的正在執(zhí)行的程序
(資料圖片僅供參考)
線程:一個(gè)進(jìn)程的最基本的執(zhí)行單位,執(zhí)行路徑
多進(jìn)程:在操作系統(tǒng)中,同時(shí)運(yùn)行多個(gè)程序
多進(jìn)程的好處:可以充分利用CPU,提高CPU的使用率
多線程:在同一個(gè)進(jìn)程(應(yīng)用程序)中同時(shí)執(zhí)行多個(gè)線程
多線程的好處:提高進(jìn)程的執(zhí)行使用率,提高了CPU的使用率
注意:
在同一個(gè)時(shí)間點(diǎn)一個(gè)CPU中只可能有一個(gè)線程在執(zhí)行
多線程不能提高效率、反而會(huì)降低效率,但是可以提高CPU的使用率
一個(gè)進(jìn)程如果有多條執(zhí)行路徑,則稱為多線程程序
Java虛擬機(jī)的啟動(dòng)至少開(kāi)啟了兩條線程,主線程和垃圾回收線程
一個(gè)線程可以理解為進(jìn)程的子任務(wù)
2.說(shuō)說(shuō)Java中實(shí)現(xiàn)多線程的幾種方法
Thread對(duì)象就是一個(gè)線程
創(chuàng)建線程的常用三種方式:
繼承Thread類
實(shí)現(xiàn)Runnable接口
實(shí)現(xiàn)Callable接口(JDK1.5>=)
線程池方式創(chuàng)建
通過(guò)繼承Thread類或者實(shí)現(xiàn)Runnable接口、Callable接口都可以實(shí)現(xiàn)多線程,不過(guò)實(shí)現(xiàn)Runnable接口與實(shí)現(xiàn)Callable接口的方式基本相同,只是Callable接口里定義的方法返回值,可以聲明拋出異常而已。因此將實(shí)現(xiàn)Runnable接口和實(shí)現(xiàn)Callable接口歸為一種方式。這種方式與繼承Thread方式之間的主要差別如下。
繼承Thread類
實(shí)現(xiàn)的步驟:
創(chuàng)建Thread類的子類
重寫(xiě)run方法
創(chuàng)建線程對(duì)象
啟動(dòng)線程
案例代碼
注意點(diǎn):
啟動(dòng)線程是使用start方法而不是run方法
線程不能啟動(dòng)多次,如果要?jiǎng)?chuàng)建多個(gè)線程,那么就需要?jiǎng)?chuàng)建多個(gè)Thread對(duì)象
實(shí)現(xiàn)Runnable接口
在第一種實(shí)現(xiàn)方式中,我們是將線程的創(chuàng)建和線程執(zhí)行的業(yè)務(wù)都封裝在了Thread對(duì)象中,我們可以通過(guò)Runable接口來(lái)實(shí)現(xiàn)線程程序代碼和數(shù)據(jù)有效的分離。
實(shí)現(xiàn)的步驟:
創(chuàng)建Runable的實(shí)現(xiàn)類
重寫(xiě)run方法
創(chuàng)建Runable實(shí)例對(duì)象(通過(guò)實(shí)現(xiàn)類來(lái)實(shí)現(xiàn))
創(chuàng)建Thread對(duì)象,并把第三部的Runable實(shí)現(xiàn)作為T(mén)hread構(gòu)造方法的參數(shù)
啟動(dòng)線程
實(shí)現(xiàn)Runable接口的好處:
1. 可以避免Java單繼承帶來(lái)的局限性
2. 適合多個(gè)相同的程序代碼處理同一個(gè)資源的情況,把線程同程序的代碼和數(shù)據(jù)有效的分離,較好的體現(xiàn)了面向?qū)ο蟮脑O(shè)計(jì)思想
Callable的方式
  前面我們介紹的兩種創(chuàng)建線程的方式都是重寫(xiě)run方法,而且run方法是沒(méi)有返回結(jié)果的,也就是main方法是不知道開(kāi)啟的線程什么時(shí)候開(kāi)始執(zhí)行,什么時(shí)候結(jié)束執(zhí)行,也獲取不到對(duì)應(yīng)的返回結(jié)果。而且run方法也不能把可能產(chǎn)生的異常拋出。在JDK1.5之后推出了通過(guò)實(shí)現(xiàn)Callable接口的方式來(lái)創(chuàng)建新的線程,這種方式可以獲取對(duì)應(yīng)的返回結(jié)果
實(shí)現(xiàn)Runnable接口和實(shí)現(xiàn)Callable接口的區(qū)別:
Runnable是自從java1.1就有了,而Callable是1.5之后才加上去的
Callable規(guī)定的方法是call(),Runnable規(guī)定的方法是run()
Callable的任務(wù)執(zhí)行后可返回值,而Runnable的任務(wù)是不能返回值(是void)
call方法可以拋出異常,run方法不可以
運(yùn)行Callable任務(wù)可以拿到一個(gè)Future對(duì)象,表示異步計(jì)算的結(jié)果。它提供了檢查計(jì)算是否完成的方法,以等待計(jì)算的完成,并檢索計(jì)算的結(jié)果。通過(guò)Future對(duì)象可以了解任務(wù)執(zhí)行情況,可取消任務(wù)的執(zhí)行,還可獲取執(zhí)行結(jié)果。
加入線程池運(yùn)行,Runnable使用ExecutorService的execute方法,Callable使用submit方法。
其實(shí)Callable接口底層的實(shí)現(xiàn)就是對(duì)Runable接口實(shí)現(xiàn)的封裝,線程啟動(dòng)執(zhí)行的也是Runable接口實(shí)現(xiàn)中的run方法,只是在run方法中有調(diào)用call方法罷了
3.如何停止一個(gè)正在運(yùn)行的線程
設(shè)置標(biāo)志位:如果線程的run方法中執(zhí)行的是一個(gè)重復(fù)執(zhí)行的循環(huán),可以提供一個(gè)標(biāo)記來(lái)控制循環(huán)是否繼續(xù)
利用中斷標(biāo)志位: 在線程中有個(gè)中斷的標(biāo)志位,默認(rèn)是false,當(dāng)我們顯示的調(diào)用 interrupted方法或者isInterrupted方法是會(huì)修改標(biāo)志位為true。我們可以利用此來(lái)中斷運(yùn)行的線程。
利用InterruptedException:如果線程因?yàn)閳?zhí)行join(),sleep()或者wait()而進(jìn)入阻塞狀態(tài),此時(shí)要想停止它,可以讓他調(diào)用interrupt(),程序會(huì)拋出InterruptedException異常。我們利用這個(gè)異??梢詠?lái)終止線程。
4.介紹下線程中的常用方法
1.start方法
start方法是我們開(kāi)啟一個(gè)新的線程的方法,但是并不是直接開(kāi)啟,而是告訴CPU我已經(jīng)準(zhǔn)備好了,快點(diǎn)運(yùn)行我,這是啟動(dòng)一個(gè)線程的唯一入口。
2.run方法
線程的線程體,當(dāng)一個(gè)線程開(kāi)始運(yùn)行后,執(zhí)行的就是run方法里面的代碼,我們不能直接通過(guò)線程對(duì)象來(lái)調(diào)用run方法。因?yàn)檫@并沒(méi)有產(chǎn)生一個(gè)新的線程。僅僅只是一個(gè)普通對(duì)象的方法調(diào)用。
3.getName方法
獲取線程名稱的方法
4.優(yōu)先級(jí)
我們創(chuàng)建的多個(gè)線程的執(zhí)行順序是由CPU決定的。Java中提供了一個(gè)線程調(diào)度器來(lái)監(jiān)控程序中啟動(dòng)后進(jìn)入就緒狀態(tài)的所有的線程,優(yōu)先級(jí)高的線程會(huì)獲取到比較多
運(yùn)行機(jī)會(huì)
大家會(huì)發(fā)現(xiàn),設(shè)置了優(yōu)先級(jí)后輸出的結(jié)果和我們預(yù)期的并不一樣,這是為什么呢??jī)?yōu)先級(jí)在CPU調(diào)動(dòng)線程執(zhí)行的時(shí)候會(huì)是一個(gè)參考因數(shù),但不是決定因數(shù),
5.sleep方法
將當(dāng)前線程暫定指定的時(shí)間,
6.isAlive
獲取線程的狀態(tài)。
輸出結(jié)果
7.join
調(diào)用某線程的該方法,將當(dāng)前線程和該線程合并,即等待該線程結(jié)束,在恢復(fù)當(dāng)前線程的運(yùn)行
輸出結(jié)果:
8.yield
讓出CPU,當(dāng)前線程進(jìn)入就緒狀態(tài)
9.wait和notify/notifyAll
阻塞和喚醒的方法,是Object中的方法,我們?cè)跀?shù)據(jù)同步的時(shí)候會(huì)介紹到
5.介紹下線程的生命周期
生命周期:對(duì)象從創(chuàng)建到銷毀的全過(guò)程
線程的生命周期:線程對(duì)象(Thread)從開(kāi)始到銷毀的全過(guò)程
線程的狀態(tài):
創(chuàng)建 ?Thread對(duì)象
就緒狀態(tài) ?執(zhí)行start方法后線程進(jìn)入可運(yùn)行的狀態(tài)
運(yùn)行狀態(tài) CPU運(yùn)行
阻塞狀態(tài) ?運(yùn)行過(guò)程中被中斷(等待阻塞,對(duì)象鎖阻塞,其他阻塞)
終止?fàn)顟B(tài) ?線程執(zhí)行完成
6.為什么wait, notify和notifyAll這些方法不在thread類里面?
明顯的原因是JAVA提供的鎖是對(duì)象級(jí)的而不是線程級(jí)的,每個(gè)對(duì)象都有鎖,通過(guò)線程獲得。如果線程需要等待某些鎖那么調(diào)用對(duì)象中的wait()方法就有意義了。如果wait()方法定義在Thread類中,線程正在等待的是哪個(gè)鎖就不明顯了。簡(jiǎn)單的說(shuō),由于wait,notify和notifyAll都是鎖級(jí)別的操作,所以把他們定義在Object類中因?yàn)殒i屬于對(duì)象。
7.為什么wait和notify方法要在同步塊中調(diào)用?
1.只有在調(diào)用線程擁有某個(gè)對(duì)象的獨(dú)占鎖時(shí),才能夠調(diào)用該對(duì)象的wait(),notify()和notifyAll()方法。2.如果你不這么做,你的代碼會(huì)拋出IllegalMonitorStateException異常。3.還有一個(gè)原因是為了避免wait和notify之間產(chǎn)生競(jìng)態(tài)條件。??wait()方法強(qiáng)制當(dāng)前線程釋放對(duì)象鎖。這意味著在調(diào)用某對(duì)象的wait()方法之前,當(dāng)前線程必須已經(jīng)獲得該對(duì)象的鎖。因此,線程必須在某個(gè)對(duì)象的同步方法或同步代碼塊中才能調(diào)用該對(duì)象的wait()方法。??在調(diào)用對(duì)象的notify()和notifyAll()方法之前,調(diào)用線程必須已經(jīng)得到該對(duì)象的鎖。因此,必須在某個(gè)對(duì)象的同步方法或同步代碼塊中才能調(diào)用該對(duì)象的notify()或notifyAll()方法。??調(diào)用wait()方法的原因通常是,調(diào)用線程希望某個(gè)特殊的狀態(tài)(或變量)被設(shè)置之后再繼續(xù)執(zhí)行。調(diào)用notify() 或notifyAll()方法的原因通常是,調(diào)用線程希望告訴其他等待中的線程:"特殊狀態(tài)已經(jīng)被設(shè)置"。這個(gè)狀態(tài)作為線程間通信的通道,它必須是一個(gè)可變的共享狀態(tài)(或變量)。
8.synchronized和ReentrantLock的區(qū)別
相似點(diǎn):??這兩種同步方式有很多相似之處,它們都是加鎖方式同步,而且都是阻塞式的同步,也就是說(shuō)當(dāng)如果一個(gè)線程獲得了對(duì)象鎖,進(jìn)入了同步塊,其他訪問(wèn)該同步塊的線程都必須阻塞在同步塊外面等待,而進(jìn)行線程阻塞和喚醒的代價(jià)是比較高的.區(qū)別:??這兩種方式最大區(qū)別就是對(duì)于Synchronized來(lái)說(shuō),它是java語(yǔ)言的關(guān)鍵字,是原生語(yǔ)法層面的互斥,需要jvm實(shí)現(xiàn)。而ReentrantLock它是JDK 1.5之后提供的API層面的互斥鎖,需要lock()和unlock()方法配合try/finally語(yǔ)句塊來(lái)完成。
Synchronized進(jìn)過(guò)編譯,會(huì)在同步塊的前后分別形成monitorenter和monitorexit這個(gè)兩個(gè)字節(jié)碼指令。在執(zhí)行monitorenter指令時(shí),首先要嘗試獲取對(duì)象鎖。如果這個(gè)對(duì)象沒(méi)被鎖定,或者當(dāng)前線程已經(jīng)擁有了那個(gè)對(duì)象鎖,把鎖的計(jì)算器加1,相應(yīng)的,在執(zhí)行monitorexit指令時(shí)會(huì)將鎖計(jì)算器就減1,當(dāng)計(jì)算器為0時(shí),鎖就被釋放了。如果獲取對(duì)象鎖失敗,那當(dāng)前線程就要阻塞,直到對(duì)象鎖被另一個(gè)線程釋放為止。
由于ReentrantLock是java.util.concurrent包下提供的一套互斥鎖,相比Synchronized,ReentrantLock類提供了一些高級(jí)功能,主要有以下3項(xiàng):
1.等待可中斷,持有鎖的線程長(zhǎng)期不釋放的時(shí)候,正在等待的線程可以選擇放棄等待,這相當(dāng)于Synchronized來(lái)說(shuō)可以避免出現(xiàn)死鎖的情況。
2.公平鎖,多個(gè)線程等待同一個(gè)鎖時(shí),必須按照申請(qǐng)鎖的時(shí)間順序獲得鎖,Synchronized鎖非公平鎖,ReentrantLock默認(rèn)的構(gòu)造函數(shù)是創(chuàng)建的非公平鎖,可以通過(guò)參數(shù)true設(shè)為公平鎖,但公平鎖表現(xiàn)的性能不是很好。
3.鎖綁定多個(gè)條件,一個(gè)ReentrantLock對(duì)象可以同時(shí)綁定對(duì)個(gè)對(duì)象
9.什么是線程安全
線程安全就是說(shuō)多線程訪問(wèn)同一段代碼,不會(huì)產(chǎn)生不確定的結(jié)果。??如果你的代碼在多線程下執(zhí)行和在單線程下執(zhí)行永遠(yuǎn)都能獲得一樣的結(jié)果,那么你的代碼就是線程安全的。這個(gè)問(wèn)題有值得一提的地方,就是線程安全也是有幾個(gè)級(jí)別的:(1)不可變??像String、Integer、Long這些,都是final類型的類,任何一個(gè)線程都改變不了它們的值,要改變除非新創(chuàng)建一個(gè),因此這些不可變對(duì)象不需要任何同步手段就可以直接在多線程環(huán)境下使用(2)絕對(duì)線程安全??不管運(yùn)行時(shí)環(huán)境如何,調(diào)用者都不需要額外的同步措施。要做到這一點(diǎn)通常需要付出許多額外的代價(jià),Java中標(biāo)注自己是線程安全的類,實(shí)際上絕大多數(shù)都不是線程安全的,不過(guò)絕對(duì)線程安全的類,Java中也有,比方說(shuō)CopyOnWriteArrayList、CopyOnWriteArraySet(3)相對(duì)線程安全??相對(duì)線程安全也就是我們通常意義上所說(shuō)的線程安全,像Vector這種,add、remove方法都是原子操作,不會(huì)被打斷,但也僅限于此,如果有個(gè)線程在遍歷某個(gè)Vector、有個(gè)線程同時(shí)在add這個(gè)Vector,99%的情況下都會(huì)出現(xiàn)ConcurrentModificationException,也就是fail-fast機(jī)制。(4)線程非安全??這個(gè)就沒(méi)什么好說(shuō)的了,ArrayList、LinkedList、HashMap等都是線程非安全的類
10.Thread類中yield方法的作用
yield方法可以暫停當(dāng)前正在執(zhí)行的線程對(duì)象,讓其它有相同優(yōu)先級(jí)的線程執(zhí)行。它是一個(gè)靜態(tài)方法而且只保證當(dāng)前線程放棄CPU占用而不能保證使其它線程一定能占用CPU,執(zhí)行yield()的線程有可能在進(jìn)入到暫停狀態(tài)后馬上又被執(zhí)行。
11.常用的線程池有哪些
new SingleThreadExecutor:創(chuàng)建一個(gè)單線程的線程池,此線程池保證所有任務(wù)的執(zhí)行順序按照任務(wù)的提交順序執(zhí)行。new FixedThreadPool:創(chuàng)建固定大小的線程池,每次提交一個(gè)任務(wù)就創(chuàng)建一個(gè)線程,直到線程達(dá)到線程池的最大大小。new CachedThreadPool:創(chuàng)建一個(gè)可緩存的線程池,此線程池不會(huì)對(duì)線程池大小做限制,線程池大小完全依賴于操作系統(tǒng)(或者說(shuō)JVM)能夠創(chuàng)建的最大線程大小。new ScheduledThreadPool:創(chuàng)建一個(gè)大小無(wú)限的線程池,此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)的求。
12. 簡(jiǎn)述一下你對(duì)線程池的理解
如果問(wèn)到了這樣的問(wèn)題,可以展開(kāi)的說(shuō)一下線程池如何用、線程池的好處、線程池的啟動(dòng)策略合理利用線程池能夠帶來(lái)三個(gè)好處。第一:降低資源消耗。通過(guò)重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗。
第二:提高響應(yīng)速度。當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不需要等到線程創(chuàng)建就能立即執(zhí)行。第三:提高線程的可管理性。線程是稀缺資源,如果無(wú)限制的創(chuàng)建,不僅會(huì)消耗系統(tǒng)資源,還會(huì)降低系統(tǒng)的穩(wěn)定性,使用線程池可以進(jìn)行統(tǒng)一的分配,調(diào)優(yōu)和監(jiān)控。
參數(shù)含義:
線程池工作原理:
提交一個(gè)任務(wù)到線程池中,線程池的處理流程如下:
判斷線程池里的核心線程是否都在執(zhí)行任務(wù),如果不是(核心線程空閑或者還有核心線程沒(méi)有被創(chuàng)建)則創(chuàng)建一個(gè)新的工作線程來(lái)執(zhí)行任務(wù)。如果核心線程都在執(zhí)行任務(wù),則進(jìn)入下個(gè)流程。
線程池判斷工作隊(duì)列是否已滿,如果工作隊(duì)列沒(méi)有滿,則將新提交的任務(wù)存儲(chǔ)在這個(gè)工作隊(duì)列里。如果工作隊(duì)列滿了,則進(jìn)入下個(gè)流程。
判斷線程池里的線程是否都處于工作狀態(tài),如果沒(méi)有,則創(chuàng)建一個(gè)新的工作線程來(lái)執(zhí)行任務(wù)。如果已經(jīng)滿了,則交給飽和策略來(lái)處理這個(gè)任務(wù)。
13.線程池的拒絕策略有哪些?
主要有4種拒絕策略:
AbortPolicy:直接丟棄任務(wù),拋出異常,這是默認(rèn)策略
CallerRunsPolicy:只用調(diào)用者所在的線程來(lái)處理任務(wù)
DiscardOldestPolicy:丟棄等待隊(duì)列中最舊的任務(wù),并執(zhí)行當(dāng)前任務(wù)
DiscardPolicy:直接丟棄任務(wù),也不拋出異常
14.線程安全需要保證幾個(gè)基本特性?
原子性,簡(jiǎn)單說(shuō)就是相關(guān)操作不會(huì)中途被其他線程干擾,一般通過(guò)同步機(jī)制實(shí)現(xiàn)。可見(jiàn)性,是一個(gè)線程修改了某個(gè)共享變量,其狀態(tài)能夠立即被其他線程知曉,通常被解釋為將線程本地狀態(tài)反映到主內(nèi)存上,volatile就是負(fù)責(zé)保證可見(jiàn)性的。有序性,是保證線程內(nèi)串行語(yǔ)義,避免指令重排等。
15.說(shuō)下線程間是如何通信的?
線程之間的通信有兩種方式:共享內(nèi)存和消息傳遞。
共享內(nèi)存在共享內(nèi)存的并發(fā)模型里,線程之間共享程序的公共狀態(tài),線程之間通過(guò)寫(xiě)-讀內(nèi)存中的公共狀態(tài)來(lái)隱式進(jìn)行通信。典型的共享內(nèi)存通信方式,就是通過(guò)共享對(duì)象進(jìn)行通信。
例如線程A與線程B之間如果要通信的話,那么就必須經(jīng)歷下面兩個(gè)步驟:1.線程A把本地內(nèi)存A更新過(guò)得共享變量刷新到主內(nèi)存中去。2.線程B到主內(nèi)存中去讀取線程A之前更新過(guò)的共享變量。消息傳遞在消息傳遞的并發(fā)模型里,線程之間沒(méi)有公共狀態(tài),線程之間必須通過(guò)明確的發(fā)送消息來(lái)顯式進(jìn)行通信。在Java中典型的消息傳遞方式,就是wait()和notify(),或者BlockingQueue
16.說(shuō)說(shuō)ThreadLocal的原理
ThreadLocal可以理解為線程本地變量,他會(huì)在每個(gè)線程都創(chuàng)建一個(gè)副本,那么在線程之間訪問(wèn)內(nèi)部副本變量就行了,做到了線程之間互相隔離,相比于synchronized的做法是用空間來(lái)?yè)Q時(shí)間。
ThreadLocal有一個(gè)靜態(tài)內(nèi)部類ThreadLocalMap,ThreadLocalMap又包含了一個(gè)Entry數(shù)組,Entry本身是一個(gè)弱引用,他的key是指向ThreadLocal的弱引用,Entry具備了保存key value鍵值對(duì)的能力。
弱引用的目的是為了防止內(nèi)存泄露,如果是強(qiáng)引用那么ThreadLocal對(duì)象除非線程結(jié)束否則始終無(wú)法被回收,弱引用則會(huì)在下一次GC的時(shí)候被回收。
但是這樣還是會(huì)存在內(nèi)存泄露的問(wèn)題,假如key和ThreadLocal對(duì)象被回收之后,entry中就存在key為null,但是value有值的entry對(duì)象,但是永遠(yuǎn)沒(méi)辦法被訪問(wèn)到,同樣除非線程結(jié)束運(yùn)行。
但是只要ThreadLocal使用恰當(dāng),在使用完之后調(diào)用remove方法刪除Entry對(duì)象,實(shí)際上是不會(huì)出現(xiàn)這個(gè)問(wèn)題的。
17.解釋下:同步、異步、阻塞、非阻塞
同步和異步指的是:當(dāng)前線程是否需要等待方法調(diào)用執(zhí)行完畢。
阻塞和非阻塞指的是:當(dāng)前接口數(shù)據(jù)還未準(zhǔn)備就緒時(shí),線程是否被阻塞掛起
同步&異步其實(shí)是處于框架這種高層次維度來(lái)看待的,而阻塞&非阻塞往往針對(duì)底層的系統(tǒng)調(diào)用方面來(lái)抉擇,也就是說(shuō)兩者是從不同維度來(lái)考慮的。
這四個(gè)概念兩兩組合,會(huì)形成4個(gè)新的概念,如下:
同步阻塞:客戶端發(fā)送請(qǐng)求給服務(wù)端,此時(shí)服務(wù)端處理任務(wù)時(shí)間很久,則客戶端則被服務(wù)端堵塞了,所以客戶端會(huì)一直等待服務(wù)端的響應(yīng),此時(shí)客戶端不能做其他任何事,服務(wù)端也不會(huì)接受其他客戶端的請(qǐng)求。這種通信機(jī)制比較簡(jiǎn)單粗暴,但是效率不高。
同步非阻塞:客戶端發(fā)送請(qǐng)求給服務(wù)端,此時(shí)服務(wù)端處理任務(wù)時(shí)間很久,這個(gè)時(shí)候雖然客戶端會(huì)一直等待響應(yīng),但是服務(wù)端可以處理其他的請(qǐng)求,過(guò)一會(huì)回來(lái)處理原先的。這種方式很高效,一個(gè)服務(wù)端可以處理很多請(qǐng)求,不會(huì)在因?yàn)槿蝿?wù)沒(méi)有處理完而堵著,所以這是非阻塞的。
異步阻塞:客戶端發(fā)送請(qǐng)求給服務(wù)端,此時(shí)服務(wù)端處理任務(wù)時(shí)間很久,但是客戶端不會(huì)等待服務(wù)器響應(yīng),它可以做其他的任務(wù),等服務(wù)器處理完畢后再把結(jié)果響應(yīng)給客戶端,客戶端得到回調(diào)后再處理服務(wù)端的響應(yīng)。這種方式可以避免客戶端一直處于等待的狀態(tài),優(yōu)化了用戶體驗(yàn),其實(shí)就是類似于網(wǎng)頁(yè)里發(fā)起的ajax異步請(qǐng)求。
異步非阻塞:客戶端發(fā)送請(qǐng)求給服務(wù)端,此時(shí)服務(wù)端處理任務(wù)時(shí)間很久,這個(gè)時(shí)候的任務(wù)雖然處理時(shí)間會(huì)很久,但是客戶端可以做其他的任務(wù),因?yàn)樗钱惒降?,可以在回調(diào)函數(shù)里處理響應(yīng);同時(shí)服務(wù)端是非阻塞的,所以服務(wù)端可以去處理其他的任務(wù),如此,這個(gè)模式就顯得非常的高效了。
18.什么是BIO?
BIO: 同步并阻塞,服務(wù)器實(shí)現(xiàn)一個(gè)連接一個(gè)線程,即客戶端有連接請(qǐng)求時(shí)服務(wù)器端就需要啟動(dòng)一個(gè)線程進(jìn)行處理,沒(méi)處理完之前此線程不能做其他操作(如果是單線程的情況下,我傳輸?shù)奈募艽竽??),?dāng)然可以通過(guò)線程池機(jī)制改善。
BIO方式 適用于連接數(shù)目比較小且固定的架構(gòu),這種方式對(duì)服務(wù)器資源要求比較高,并發(fā)局限于應(yīng)用中JDK1.4以前的唯一選擇,但程序直觀簡(jiǎn)單易理解。
19.什么是NIO?
NIO: 同步非阻塞,服務(wù)器實(shí)現(xiàn)一個(gè)連接一個(gè)線程,即客戶端發(fā)送的連接請(qǐng)求都會(huì)注冊(cè)到多路復(fù)用器上,多復(fù)用器輪詢到連接有I/O請(qǐng)求時(shí)才啟動(dòng)一個(gè)線程進(jìn)行處理。
NIO方式 適用于連接數(shù)目多且連接比較短(輕操作)的架構(gòu),比如聊天服務(wù)器,并發(fā)局限于應(yīng)用中,編程比較復(fù)雜,JDK1.4之后開(kāi)始支持。
20.什么是AIO?
AIO: 異步非阻塞,服務(wù)器實(shí)現(xiàn)模式為一個(gè)有效請(qǐng)求一個(gè)線程,客戶端的I/O請(qǐng)求都是由操作系統(tǒng)先完成了再通知服務(wù)器應(yīng)用去啟動(dòng)線程進(jìn)行處理,AIO方式使用于連接數(shù)目多且連接比較長(zhǎng)(重操作)的架構(gòu),比如相冊(cè)服務(wù)器,充分調(diào)用操作系統(tǒng)參與并發(fā)操作,編程比較復(fù)雜,JDK1.7之后開(kāi)始支持。
AIO屬于NIO包中的類實(shí)現(xiàn),其實(shí) IO主要分為BIO和NIO,AIO只是附加品,解決IO不能異步的實(shí)現(xiàn)在以前很少有Linux系統(tǒng)支持AIO,Windows的IOCP就是該AIO模型。但是現(xiàn)在的服務(wù)器一般都是支持AIO操作
21.介紹下IO流的分類
關(guān)鍵詞: 執(zhí)行任務(wù) 啟動(dòng)線程