最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

Java基礎(chǔ)面試題詳解2(拒絕死記硬背八股文)

2023-02-10 01:39 作者:茶曉i  | 我要投稿

一、IO和多線程專題

1.介紹下進程和線程的關(guān)系

進程:一個獨立的正在執(zhí)行的程序

線程:一個進程的最基本的執(zhí)行單位,執(zhí)行路徑

image.png

多進程:在操作系統(tǒng)中,同時運行多個程序

多進程的好處:可以充分利用CPU,提高CPU的使用率

多線程:在同一個進程(應(yīng)用程序)中同時執(zhí)行多個線程

多線程的好處:提高進程的執(zhí)行使用率,提高了CPU的使用率

注意:

  1. 在同一個時間點一個CPU中只可能有一個線程在執(zhí)行

  2. 多線程不能提高效率、反而會降低效率,但是可以提高CPU的使用率

  3. 一個進程如果有多條執(zhí)行路徑,則稱為多線程程序

  4. Java虛擬機的啟動至少開啟了兩條線程,主線程和垃圾回收線程

  5. 一個線程可以理解為進程的子任務(wù)

2.說說Java中實現(xiàn)多線程的幾種方法

Thread對象就是一個線程

創(chuàng)建線程的常用三種方式:

  1. 繼承Thread類

  2. 實現(xiàn)Runnable接口

  3. 實現(xiàn)Callable接口(JDK1.5>=)

  4. 線程池方式創(chuàng)建

??通過繼承Thread類或者實現(xiàn)Runnable接口、Callable接口都可以實現(xiàn)多線程,不過實現(xiàn)Runnable接口與實現(xiàn)Callable接口的方式基本相同,只是Callable接口里定義的方法返回值,可以聲明拋出異常而已。因此將實現(xiàn)Runnable接口和實現(xiàn)Callable接口歸為一種方式。這種方式與繼承Thread方式之間的主要差別如下。

繼承Thread類

實現(xiàn)的步驟:

  1. 創(chuàng)建Thread類的子類

  2. 重寫run方法

  3. 創(chuàng)建線程對象

  4. 啟動線程

案例代碼

注意點:

  1. 啟動線程是使用start方法而不是run方法

  2. 線程不能啟動多次,如果要創(chuàng)建多個線程,那么就需要創(chuàng)建多個Thread對象

image.png

實現(xiàn)Runnable接口

??在第一種實現(xiàn)方式中,我們是將線程的創(chuàng)建和線程執(zhí)行的業(yè)務(wù)都封裝在了Thread對象中,我們可以通過Runable接口來實現(xiàn)線程程序代碼和數(shù)據(jù)有效的分離。

實現(xiàn)的步驟:

  1. 創(chuàng)建Runable的實現(xiàn)類

  2. 重寫run方法

  3. 創(chuàng)建Runable實例對象(通過實現(xiàn)類來實現(xiàn))

  4. 創(chuàng)建Thread對象,并把第三部的Runable實現(xiàn)作為Thread構(gòu)造方法的參數(shù)

  5. 啟動線程

實現(xiàn)Runable接口的好處:


1. 可以避免Java單繼承帶來的局限性

2. 適合多個相同的程序代碼處理同一個資源的情況,把線程同程序的代碼和數(shù)據(jù)有效的分離,較好的體現(xiàn)了面向?qū)ο蟮脑O(shè)計思想

Callable的方式

  前面我們介紹的兩種創(chuàng)建線程的方式都是重寫run方法,而且run方法是沒有返回結(jié)果的,也就是main方法是不知道開啟的線程什么時候開始執(zhí)行,什么時候結(jié)束執(zhí)行,也獲取不到對應(yīng)的返回結(jié)果。而且run方法也不能把可能產(chǎn)生的異常拋出。在JDK1.5之后推出了通過實現(xiàn)Callable接口的方式來創(chuàng)建新的線程,這種方式可以獲取對應(yīng)的返回結(jié)果

實現(xiàn)Runnable接口和實現(xiàn)Callable接口的區(qū)別:

  1. Runnable是自從java1.1就有了,而Callable是1.5之后才加上去的

  2. Callable規(guī)定的方法是call(),Runnable規(guī)定的方法是run()

  3. Callable的任務(wù)執(zhí)行后可返回值,而Runnable的任務(wù)是不能返回值(是void)

  4. call方法可以拋出異常,run方法不可以

  5. 運行Callable任務(wù)可以拿到一個Future對象,表示異步計算的結(jié)果。它提供了檢查計算是否完成的方法,以等待計算的完成,并檢索計算的結(jié)果。通過Future對象可以了解任務(wù)執(zhí)行情況,可取消任務(wù)的執(zhí)行,還可獲取執(zhí)行結(jié)果。

  6. 加入線程池運行,Runnable使用ExecutorService的execute方法,Callable使用submit方法。

其實Callable接口底層的實現(xiàn)就是對Runable接口實現(xiàn)的封裝,線程啟動執(zhí)行的也是Runable接口實現(xiàn)中的run方法,只是在run方法中有調(diào)用call方法罷了

3.如何停止一個正在運行的線程

設(shè)置標志位:如果線程的run方法中執(zhí)行的是一個重復(fù)執(zhí)行的循環(huán),可以提供一個標記來控制循環(huán)是否繼續(xù)

利用中斷標志位: 在線程中有個中斷的標志位,默認是false,當我們顯示的調(diào)用 interrupted方法或者isInterrupted方法是會修改標志位為true。我們可以利用此來中斷運行的線程。

利用InterruptedException: 如果線程因為執(zhí)行join(),sleep()或者wait()而進入阻塞狀態(tài),此時要想停止它,可以讓他調(diào)用interrupt(),程序會拋出InterruptedException異常。我們利用這個異??梢詠斫K止線程。

4.介紹下線程中的常用方法

1.start方法

start方法是我們開啟一個新的線程的方法,但是并不是直接開啟,而是告訴CPU我已經(jīng)準備好了,快點運行我,這是啟動一個線程的唯一入口。

2.run方法

線程的線程體,當一個線程開始運行后,執(zhí)行的就是run方法里面的代碼,我們不能直接通過線程對象來調(diào)用run方法。因為這并沒有產(chǎn)生一個新的線程。僅僅只是一個普通對象的方法調(diào)用。

3.getName方法

獲取線程名稱的方法

4.優(yōu)先級

我們創(chuàng)建的多個線程的執(zhí)行順序是由CPU決定的。Java中提供了一個線程調(diào)度器來監(jiān)控程序中啟動后進入就緒狀態(tài)的所有的線程,優(yōu)先級高的線程會獲取到比較多

運行機會

大家會發(fā)現(xiàn),設(shè)置了優(yōu)先級后輸出的結(jié)果和我們預(yù)期的并不一樣,這是為什么呢?優(yōu)先級在CPU調(diào)動線程執(zhí)行的時候會是一個參考因數(shù),但不是決定因數(shù),

5.sleep方法

將當前線程暫定指定的時間,

6.isAlive

獲取線程的狀態(tài)。

輸出結(jié)果

7.join

調(diào)用某線程的該方法,將當前線程和該線程合并,即等待該線程結(jié)束,在恢復(fù)當前線程的運行

輸出結(jié)果:

8.yield

讓出CPU,當前線程進入就緒狀態(tài)

9.wait和notify/notifyAll

阻塞和喚醒的方法,是Object中的方法,我們在數(shù)據(jù)同步的時候會介紹到

5.介紹下線程的生命周期

生命周期:對象從創(chuàng)建到銷毀的全過程

線程的生命周期:線程對象(Thread)從開始到銷毀的全過程

image.png

線程的狀態(tài):

  1. 創(chuàng)建 ?Thread對象

  2. 就緒狀態(tài) ?執(zhí)行start方法后線程進入可運行的狀態(tài)

  3. 運行狀態(tài) CPU運行

  4. 阻塞狀態(tài) ?運行過程中被中斷(等待阻塞,對象鎖阻塞,其他阻塞)

  5. 終止狀態(tài) ?線程執(zhí)行完成

6.為什么wait, notify和notifyAll這些方法不在thread類里面?

??明顯的原因是JAVA提供的鎖是對象級的而不是線程級的,每個對象都有鎖,通過線程獲得。如果線程需要等待某些鎖那么調(diào)用對象中的wait()方法就有意義了。如果wait()方法定義在Thread類中,線程正在等待的是哪個鎖就不明顯了。簡單的說,由于wait,notify和notifyAll都是鎖級別的操作,所以把他們定義在Object類中因為鎖屬于對象。

7.為什么wait和notify方法要在同步塊中調(diào)用?

1.只有在調(diào)用線程擁有某個對象的獨占鎖時,才能夠調(diào)用該對象的wait(),notify()和notifyAll()方法。2.如果你不這么做,你的代碼會拋出IllegalMonitorStateException異常。3.還有一個原因是為了避免wait和notify之間產(chǎn)生競態(tài)條件。??wait()方法強制當前線程釋放對象鎖。這意味著在調(diào)用某對象的wait()方法之前,當前線程必須已經(jīng)獲得該對象的鎖。因此,線程必須在某個對象的同步方法或同步代碼塊中才能調(diào)用該對象的wait()方法。??在調(diào)用對象的notify()notifyAll()方法之前,調(diào)用線程必須已經(jīng)得到該對象的鎖。因此,必須在某個對象的同步方法或同步代碼塊中才能調(diào)用該對象的notify()或notifyAll()方法。??調(diào)用wait()方法的原因通常是,調(diào)用線程希望某個特殊的狀態(tài)(或變量)被設(shè)置之后再繼續(xù)執(zhí)行。調(diào)用notify() 或notifyAll()方法的原因通常是,調(diào)用線程希望告訴其他等待中的線程:"特殊狀態(tài)已經(jīng)被設(shè)置"。這個狀態(tài)作為線程間通信的通道,它必須是一個可變的共享狀態(tài)(或變量)。

8.synchronized和ReentrantLock的區(qū)別

相似點??這兩種同步方式有很多相似之處,它們都是加鎖方式同步,而且都是阻塞式的同步,也就是說當如果一個線程獲得了對象鎖,進入了同步塊,其他訪問該同步塊的線程都必須阻塞在同步塊外面等待,而進行線程阻塞和喚醒的代價是比較高的.區(qū)別??這兩種方式最大區(qū)別就是對于Synchronized來說,它是java語言的關(guān)鍵字,是原生語法層面的互斥,需要jvm實現(xiàn)。而ReentrantLock它是JDK 1.5之后提供的API層面的互斥鎖,需要lock()和unlock()方法配合try/finally語句塊來完成。

??Synchronized進過編譯,會在同步塊的前后分別形成monitorentermonitorexit這個兩個字節(jié)碼指令。在執(zhí)行monitorenter指令時,首先要嘗試獲取對象鎖。如果這個對象沒被鎖定,或者當前線程已經(jīng)擁有了那個對象鎖,把鎖的計算器加1,相應(yīng)的,在執(zhí)行monitorexit指令時會將鎖計算器就減1,當計算器為0時,鎖就被釋放了。如果獲取對象鎖失敗,那當前線程就要阻塞,直到對象鎖被另一個線程釋放為止。

由于ReentrantLock是java.util.concurrent包下提供的一套互斥鎖,相比Synchronized,ReentrantLock類提供了一些高級功能,主要有以下3項:

1.等待可中斷,持有鎖的線程長期不釋放的時候,正在等待的線程可以選擇放棄等待,這相當于Synchronized來說可以避免出現(xiàn)死鎖的情況。

2.公平鎖,多個線程等待同一個鎖時,必須按照申請鎖的時間順序獲得鎖,Synchronized鎖非公平鎖,ReentrantLock默認的構(gòu)造函數(shù)是創(chuàng)建的非公平鎖,可以通過參數(shù)true設(shè)為公平鎖,但公平鎖表現(xiàn)的性能不是很好。

3.鎖綁定多個條件,一個ReentrantLock對象可以同時綁定對個對象

9.什么是線程安全

??線程安全就是說多線程訪問同一段代碼,不會產(chǎn)生不確定的結(jié)果。??如果你的代碼在多線程下執(zhí)行和在單線程下執(zhí)行永遠都能獲得一樣的結(jié)果,那么你的代碼就是線程安全的。這個問題有值得一提的地方,就是線程安全也是有幾個級別的:(1)不可變??像String、Integer、Long這些,都是final類型的類,任何一個線程都改變不了它們的值,要改變除非新創(chuàng)建一個,因此這些不可變對象不需要任何同步手段就可以直接在多線程環(huán)境下使用(2)絕對線程安全??不管運行時環(huán)境如何,調(diào)用者都不需要額外的同步措施。要做到這一點通常需要付出許多額外的代價,Java中標注自己是線程安全的類,實際上絕大多數(shù)都不是線程安全的,不過絕對線程安全的類,Java中也有,比方說CopyOnWriteArrayList、CopyOnWriteArraySet(3)相對線程安全??相對線程安全也就是我們通常意義上所說的線程安全,像Vector這種,add、remove方法都是原子操作,不會被打斷,但也僅限于此,如果有個線程在遍歷某個Vector、有個線程同時在add這個Vector,99%的情況下都會出現(xiàn)ConcurrentModificationException,也就是fail-fast機制。(4)線程非安全??這個就沒什么好說的了,ArrayList、LinkedList、HashMap等都是線程非安全的類

10.Thread類中yield方法的作用

??yield方法可以暫停當前正在執(zhí)行的線程對象,讓其它有相同優(yōu)先級的線程執(zhí)行。它是一個靜態(tài)方法而且只保證當前線程放棄CPU占用而不能保證使其它線程一定能占用CPU,執(zhí)行yield()的線程有可能在進入到暫停狀態(tài)后馬上又被執(zhí)行。

11.常用的線程池有哪些

new SingleThreadExecutor:創(chuàng)建一個單線程的線程池,此線程池保證所有任務(wù)的執(zhí)行順序按照任務(wù)的提交順序執(zhí)行。new FixedThreadPool:創(chuàng)建固定大小的線程池,每次提交一個任務(wù)就創(chuàng)建一個線程,直到線程達到線程池的最大大小。new CachedThreadPool:創(chuàng)建一個可緩存的線程池,此線程池不會對線程池大小做限制,線程池大小完全依賴于操作系統(tǒng)(或者說JVM)能夠創(chuàng)建的最大線程大小。new ScheduledThreadPool:創(chuàng)建一個大小無限的線程池,此線程池支持定時以及周期性執(zhí)行任務(wù)的求。

12. 簡述一下你對線程池的理解

如果問到了這樣的問題,可以展開的說一下線程池如何用、線程池的好處、線程池的啟動策略合理利用線程池能夠帶來三個好處。第一:降低資源消耗。通過重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗。

第二:提高響應(yīng)速度。當任務(wù)到達時,任務(wù)可以不需要等到線程創(chuàng)建就能立即執(zhí)行。第三:提高線程的可管理性。線程是稀缺資源,如果無限制的創(chuàng)建,不僅會消耗系統(tǒng)資源,還會降低系統(tǒng)的穩(wěn)定性,使用線程池可以進行統(tǒng)一的分配,調(diào)優(yōu)和監(jiān)控。

參數(shù)含義:

線程池工作原理:

提交一個任務(wù)到線程池中,線程池的處理流程如下:

  1. 判斷線程池里的核心線程是否都在執(zhí)行任務(wù),如果不是(核心線程空閑或者還有核心線程沒有被創(chuàng)建)則創(chuàng)建一個新的工作線程來執(zhí)行任務(wù)。如果核心線程都在執(zhí)行任務(wù),則進入下個流程。

  2. 線程池判斷工作隊列是否已滿,如果工作隊列沒有滿,則將新提交的任務(wù)存儲在這個工作隊列里。如果工作隊列滿了,則進入下個流程。

  3. 判斷線程池里的線程是否都處于工作狀態(tài),如果沒有,則創(chuàng)建一個新的工作線程來執(zhí)行任務(wù)。如果已經(jīng)滿了,則交給飽和策略來處理這個任務(wù)。

image.png

13.線程池的拒絕策略有哪些?

主要有4種拒絕策略:

  1. AbortPolicy:直接丟棄任務(wù),拋出異常,這是默認策略

  2. CallerRunsPolicy:只用調(diào)用者所在的線程來處理任務(wù)

  3. DiscardOldestPolicy:丟棄等待隊列中最舊的任務(wù),并執(zhí)行當前任務(wù)

  4. DiscardPolicy:直接丟棄任務(wù),也不拋出異常

14.線程安全需要保證幾個基本特性?

原子性,簡單說就是相關(guān)操作不會中途被其他線程干擾,一般通過同步機制實現(xiàn)。可見性,是一個線程修改了某個共享變量,其狀態(tài)能夠立即被其他線程知曉,通常被解釋為將線程本地狀態(tài)反映到主內(nèi)存上,volatile就是負責保證可見性的。有序性,是保證線程內(nèi)串行語義,避免指令重排等。

15.說下線程間是如何通信的?

線程之間的通信有兩種方式:共享內(nèi)存和消息傳遞。

共享內(nèi)存??在共享內(nèi)存的并發(fā)模型里,線程之間共享程序的公共狀態(tài),線程之間通過寫-讀內(nèi)存中的公共狀態(tài)來隱式進行通信。典型的共享內(nèi)存通信方式,就是通過共享對象進行通信。

image.png

例如線程A與線程B之間如果要通信的話,那么就必須經(jīng)歷下面兩個步驟:1.線程A把本地內(nèi)存A更新過得共享變量刷新到主內(nèi)存中去。2.線程B到主內(nèi)存中去讀取線程A之前更新過的共享變量。消息傳遞??在消息傳遞的并發(fā)模型里,線程之間沒有公共狀態(tài),線程之間必須通過明確的發(fā)送消息來顯式進行通信。在Java中典型的消息傳遞方式,就是wait()和notify(),或者BlockingQueue

16.說說ThreadLocal的原理

??ThreadLocal可以理解為線程本地變量,他會在每個線程都創(chuàng)建一個副本,那么在線程之間訪問內(nèi)部副本變量就行了,做到了線程之間互相隔離,相比于synchronized的做法是用空間來換時間。

??ThreadLocal有一個靜態(tài)內(nèi)部類ThreadLocalMap,ThreadLocalMap又包含了一個Entry數(shù)組,Entry本身是一個弱引用,他的key是指向ThreadLocal的弱引用,Entry具備了保存key value鍵值對的能力。

??弱引用的目的是為了防止內(nèi)存泄露,如果是強引用那么ThreadLocal對象除非線程結(jié)束否則始終無法被回收,弱引用則會在下一次GC的時候被回收。

??但是這樣還是會存在內(nèi)存泄露的問題,假如key和ThreadLocal對象被回收之后,entry中就存在key為null,但是value有值的entry對象,但是永遠沒辦法被訪問到,同樣除非線程結(jié)束運行。

??但是只要ThreadLocal使用恰當,在使用完之后調(diào)用remove方法刪除Entry對象,實際上是不會出現(xiàn)這個問題的。

image.png

17.解釋下:同步、異步、阻塞、非阻塞

同步和異步指的是:當前線程是否需要等待方法調(diào)用執(zhí)行完畢。

阻塞和非阻塞指的是:當前接口數(shù)據(jù)還未準備就緒時,線程是否被阻塞掛起

同步&異步其實是處于框架這種高層次維度來看待的,而阻塞&非阻塞往往針對底層的系統(tǒng)調(diào)用方面來抉擇,也就是說兩者是從不同維度來考慮的。

這四個概念兩兩組合,會形成4個新的概念,如下:

同步阻塞:客戶端發(fā)送請求給服務(wù)端,此時服務(wù)端處理任務(wù)時間很久,則客戶端則被服務(wù)端堵塞了,所以客戶端會一直等待服務(wù)端的響應(yīng),此時客戶端不能做其他任何事,服務(wù)端也不會接受其他客戶端的請求。這種通信機制比較簡單粗暴,但是效率不高。

同步非阻塞:客戶端發(fā)送請求給服務(wù)端,此時服務(wù)端處理任務(wù)時間很久,這個時候雖然客戶端會一直等待響應(yīng),但是服務(wù)端可以處理其他的請求,過一會回來處理原先的。這種方式很高效,一個服務(wù)端可以處理很多請求,不會在因為任務(wù)沒有處理完而堵著,所以這是非阻塞的。

異步阻塞:客戶端發(fā)送請求給服務(wù)端,此時服務(wù)端處理任務(wù)時間很久,但是客戶端不會等待服務(wù)器響應(yīng),它可以做其他的任務(wù),等服務(wù)器處理完畢后再把結(jié)果響應(yīng)給客戶端,客戶端得到回調(diào)后再處理服務(wù)端的響應(yīng)。這種方式可以避免客戶端一直處于等待的狀態(tài),優(yōu)化了用戶體驗,其實就是類似于網(wǎng)頁里發(fā)起的ajax異步請求。

異步非阻塞:客戶端發(fā)送請求給服務(wù)端,此時服務(wù)端處理任務(wù)時間很久,這個時候的任務(wù)雖然處理時間會很久,但是客戶端可以做其他的任務(wù),因為他是異步的,可以在回調(diào)函數(shù)里處理響應(yīng);同時服務(wù)端是非阻塞的,所以服務(wù)端可以去處理其他的任務(wù),如此,這個模式就顯得非常的高效了。

image.png

18.什么是BIO?

BIO同步并阻塞 ,服務(wù)器實現(xiàn)一個連接一個線程,即客戶端有連接請求時服務(wù)器端就需要啟動一個線程進行處理,沒處理完之前此線程不能做其他操作(如果是單線程的情況下,我傳輸?shù)奈募艽竽兀浚?,當然可以通過線程池機制改善。

BIO方式 適用于連接數(shù)目比較小且固定的架構(gòu) ,這種方式對服務(wù)器資源要求比較高,并發(fā)局限于應(yīng)用中JDK1.4以前的唯一選擇,但程序直觀簡單易理解。

19.什么是NIO?

NIO同步非阻塞 ,服務(wù)器實現(xiàn)一個連接一個線程,即客戶端發(fā)送的連接請求都會注冊到多路復(fù)用器上,多復(fù)用器輪詢到連接有I/O請求時才啟動一個線程進行處理。

NIO方式 適用于連接數(shù)目多且連接比較短(輕操作)的架構(gòu) ,比如聊天服務(wù)器,并發(fā)局限于應(yīng)用中,編程比較復(fù)雜,JDK1.4之后開始支持。

20.什么是AIO?

AIO異步非阻塞 ,服務(wù)器實現(xiàn)模式為一個有效請求一個線程,客戶端的I/O請求都是由操作系統(tǒng)先完成了再通知服務(wù)器應(yīng)用去啟動線程進行處理,AIO方式使用于連接數(shù)目多且連接比較長(重操作)的架構(gòu),比如相冊服務(wù)器,充分調(diào)用操作系統(tǒng)參與并發(fā)操作,編程比較復(fù)雜,JDK1.7之后開始支持。

AIO屬于NIO包中的類實現(xiàn),其實 IO主要分為BIO和NIO ,AIO只是附加品,解決IO不能異步的實現(xiàn)在以前很少有Linux系統(tǒng)支持AIO,Windows的IOCP就是該AIO模型。但是現(xiàn)在的服務(wù)器一般都是支持AIO操作

21.介紹下IO流的分類

image.png



Java基礎(chǔ)面試題詳解2(拒絕死記硬背八股文)的評論 (共 條)

分享到微博請遵守國家法律
深水埗区| 石门县| 长宁区| 巫山县| 宣武区| 独山县| 赣榆县| 崇信县| 遂川县| 洛扎县| 烟台市| 绥宁县| 社会| 太湖县| 宜兰县| 莱州市| 邵武市| 上思县| 绥化市| 德昌县| 梅州市| 广水市| 阜新市| 顺义区| 湟中县| 临邑县| 新河县| 邻水| 奉节县| 德保县| 五莲县| 宝应县| 湟源县| 罗江县| 玛多县| 色达县| 稻城县| 黄骅市| 邹城市| 霞浦县| 老河口市|