Java核心內容面試題詳解

幂果發表於2024-03-06

前言
隨著經濟的復甦,市場逐漸回暖,曾經的金三銀四,金九銀十也慢慢迴歸,在這個節骨眼上,我們要努力學習,做好知識儲備,準備隨時接收這潑天的offer。而我利用摸魚(不是,是工作之餘)時間也整理了一份關於Java核心知識的面試題,大家有興趣,有需要的可以看看,希望能夠給大家提供一些幫助

Java基礎面試題
Java的主要特性是什麼?
Java的主要特性包括物件導向、平臺獨立性(一次編寫,到處執行)、垃圾回收機制、安全性、多執行緒等。

解釋下Java中的資料型別。
Java中的資料型別分為兩大類:基本資料型別(如int、double、char等)和引用資料型別(如類、介面、陣列等)。基本資料型別包含8種,分別是byte、short、int、long、float、double、char和boolean。

什麼是自動裝箱和拆箱?
自動裝箱是指將基本資料型別自動轉換為包裝器型別(如將int轉換為Integer),而自動拆箱則是相反的過程(如將Integer轉換為int)。這一特性從Java 5開始引入,旨在簡化程式碼。

什麼是final, finally, finalize?
final:用於修飾類、方法和變數。被final修飾的類不能被繼承,被final修飾的方法不能被重寫,被final修飾的變數是常量,其值不能被改變。

finally:用於try-catch語句塊中,無論是否發生異常,finally塊中的程式碼都會執行,常用於資源清理。

finalize:是Object類的一個方法,當垃圾回收器準備回收物件時,會先呼叫該物件的finalize方法。但不建議依賴此方法來進行資源清理,因為它不是確定的會被呼叫。

什麼是Java中的內部類,它有哪些種類?
內部類是一個定義在另一個類內部的類。Java中的內部類主要有四種:靜態內部類、區域性內部類、匿名內部類和巢狀類。靜態內部類可以獨立於外部類存在,而區域性內部類和匿名內部類則依賴於外部類的例項。

什麼是Java中的匿名內部類?
匿名內部類是沒有名字的內部類,主要用於簡化程式碼,特別是當只需要使用一次內部類的時候。它通常用於實現一個介面或繼承一個類,並直接使用該物件。

解釋一下Java中的異常處理機制。
Java中的異常處理機制主要包括try、catch、finally和throw/throws關鍵字。try塊中放置可能丟擲異常的程式碼,catch塊用於捕獲並處理try塊中丟擲的異常,finally塊無論是否發生異常都會執行,通常用於資源清理。throw用於手動丟擲異常,而throws用於宣告方法可能丟擲的異常型別。

什麼是Java中的封裝、繼承和多型?
封裝:將物件的屬性和方法隱藏在物件內部,不允許外部程式直接訪問物件的內部屬性,而是透過該物件提供的方法來訪問,以保證物件的內部狀態不被破壞。

繼承:是物件導向程式設計的一個重要特性,允許我們定義一個類作為另一個類的特殊型別,子類繼承父類的屬性和方法,並可以新增自己的新屬性和方法。

多型:是指同一個引用型別在不同的情況下表現出不同的行為。在Java中,主要透過方法的過載(overloading)和重寫(overriding)實現多型。

什麼是Java中的構造方法?它有什麼特點?
構造方法是用於初始化新建立物件的方法。它的名稱與類的名稱相同,並且沒有返回型別。構造方法的主要特點包括:

構造方法的名稱必須與類的名稱相同。

構造方法沒有返回型別,也不能使用void關鍵字。

構造方法在新建立物件時自動呼叫,以初始化物件的狀態。

一個類可以有多個構造方法,這稱為構造方法的過載。

Java中的訪問修飾符有哪些?它們的訪問許可權是怎樣的?
Java中的訪問修飾符包括private、default(包級私有)、protected和public。它們的訪問許可權如下:

private:只能被定義它的類訪問。

default(包級私有):可以被定義它的類以及同一個包中的其他類訪問。

protected:可以被定義它的類、同一個包中的其他類以及所有子類訪問。

public:可以被任何類訪問。

什麼是Java中的靜態變數和例項變數?它們之間有什麼區別?
靜態變數(Static Variables):靜態變數也被稱為類變數,它們屬於類而不是類的任何特定例項。這意味著靜態變數在類載入時被建立,並且只建立一次。無論建立多少個類的例項,靜態變數都只有一個副本。靜態變數通常透過類名來訪問。

例項變數(Instance Variables):例項變數是類的每個物件的私有副本。每當建立一個新的物件時,都會為例項變數分配新的記憶體,並且每個物件都有自己的一套例項變數的副本。例項變數只能透過物件來訪問。

解釋一下Java中的垃圾回收機制。
Java中的垃圾回收機制是一種自動記憶體管理機制,用於自動回收不再使用的物件佔用的記憶體。Java透過垃圾回收器(Garbage Collector)來跟蹤哪些物件是不可達的(即沒有引用指向它們),然後釋放這些物件的記憶體。垃圾回收機制有助於防止記憶體洩漏,並使得記憶體管理變得更加簡單。

Java中的this關鍵字和super關鍵字分別有什麼作用?
this關鍵字:在Java中,this關鍵字用於引用當前物件的例項。它可以用於訪問當前物件的屬性和方法,也可以用於區分區域性變數和類的成員變數。

super關鍵字:super關鍵字用於引用當前物件的父類。它可以用於訪問父類的屬性和方法,特別是在子類中重寫父類方法時,可以使用super關鍵字來呼叫父類中的原始方法。

Java中的介面和抽象類有什麼區別?
介面(Interface):介面是一種完全抽象的類,它只包含抽象方法和常量,不能包含例項變數和例項方法的實現一個類可以實現多個介面,介面之間用逗號分隔。介面主要用於定義行為規範,即定義一組方法,這些方法需要由實現介面的類來提供具體實現。

抽象類(Abstract Class):抽象類是一種包含抽象方法和非抽象方法的類,可以包含例項變數。一個類只能繼承一個抽象類。抽象類主要用於定義公共屬性和方法,併為子類提供公共的實現。子類繼承抽象類時,可以選擇是否實現抽象方法。

Java中的集合框架是什麼?它包含哪些主要部分?
Java中的集合框架是Java提供的一套資料結構和演算法的集合,用於儲存和操作物件集合。集合框架主要包含以下幾個部分:

介面(Interfaces):如Collection、List、Set、Map等,定義了集合的行為。

實現類(Implementation Classes):如ArrayList、HashSet、LinkedList、HashMap等,實現了上述介面並提供具體的集合實現。

演算法(Algorithms):如排序和搜尋演算法,這些演算法透過Collections類進行訪問。

迭代器(Iterators):提供了一種訪問集合元素而無需暴露其底層表示的方法。

Java中的泛型是什麼?
泛型(Generics)是Java提供的一種機制,它允許在編譯時定義集合裡元素的型別,從而提供了更強的型別檢查,並避免了執行時型別轉換異常。透過使用泛型,可以在定義集合時指定集合中元素的型別,使得集合具有型別安全性。此外,泛型還可以簡化程式碼,提高程式碼的可讀性和可維護性。

Java中的Lambda表示式是什麼?它有什麼用途?
Lambda表示式是Java 8中引入的一種新特性,它允許我們以一種簡潔的方式表示匿名函式。Lambda表示式可以被視為一個函式式介面的例項,並且可以用作引數傳遞或賦值給一個變數。Lambda表示式的語法簡潔,易於理解,使得在Java中編寫函式式程式碼變得更加容易。它主要用於簡化集合操作、事件處理、執行緒程式設計等場景。

Java中的Stream API是什麼?請簡要介紹一下它的用法。
Stream API是Java 8中引入的一種新特性,它提供了一種新的處理集合資料的方式。Stream API允許我們以宣告式方式處理資料集合,可以執行復雜的查詢和轉換操作,而無需修改底層的資料結構。透過使用Stream API,我們可以輕鬆地執行過濾、對映、排序、聚合等操作,並可以在並行環境中進行高效的計算。Stream API的語法簡潔易讀,使得程式碼更加清晰和易於維護。

Java中的異常鏈是什麼?它有什麼作用?
異常鏈(Exception Chaining)是指在一個異常處理過程中,將多個異常關聯起來形成一個異常鏈。在Java中,當一個異常丟擲時,可以將其他異常作為其原因(cause)傳遞給該異常,從而形成一個異常鏈。異常鏈的作用在於提供異常之間的層次結構,使得在處理異常時能夠了解異常發生的上下文和根本原因。透過異常鏈,我們可以方便地追蹤異常的來源,並對其進行有效的處理。

Java中的反射是什麼?它有什麼應用場景?
反射(Reflection)是Java提供的一種機制,它允許程式在執行時檢查類、介面、欄位和方法的資訊,並可以動態地建立和操作物件。反射的主要應用場景包括:

在執行時動態載入和呼叫類和方法。

訪問和修改物件的私有屬性和方法。

實現框架和庫的通用功能,如序列化、ORM框架等。

進行測試和除錯,如使用反射呼叫測試方法或訪問測試物件的內部狀態。

Java中的同步和非同步有什麼區別?
同步(Synchronous):在同步程式設計中,程式按順序執行操作,並且一個操作的執行依賴於另一個操作的結果。當呼叫一個方法或操作時,呼叫者會被阻塞,直到方法或操作完成並返回結果。同步程式設計相對簡單,但可能會降低程式的效能,因為呼叫者需要等待操作完成。

非同步(Asynchronous):在非同步程式設計中,一個操作或方法的呼叫不會阻塞呼叫者,呼叫者可以繼續執行其他任務,而操作或方法會在後臺執行。當操作完成時,通常會透過回撥函式、事件或Promise等方式通知呼叫者。非同步程式設計可以提高程式的響應性和效能,特別是當處理耗時的操作(如I/O操作、網路通訊等)時。

Java中的內部類是什麼?它有哪些型別?
內部類(Inner Class)是定義在另一個類內部的類。Java支援四種型別的內部類:

靜態內部類(Static Inner Class):靜態內部類是使用static關鍵字定義的內部類。它不能訪問外部類的非靜態成員,但可以訪問外部類的靜態成員。

成員內部類(Member Inner Class):成員內部類是最常見的內部類型別,它沒有static修飾符。它可以訪問外部類的所有成員,包括私有成員。

區域性內部類(Local Inner Class):區域性內部類定義在方法或程式碼塊中。它可以訪問外部類的所有成員以及定義它的方法或程式碼塊的區域性變數。

匿名內部類(Anonymous Inner Class):匿名內部類是沒有名字的內部類,它通常用於實現介面或擴充套件類,並且只使用匿名內部類可以簡化程式碼,但可讀性較差。

Java中的final關鍵字有哪些用法?
final關鍵字在Java中有幾種不同的用法:

修飾變數:將變數宣告為final意味著該變數的值一旦賦值後就不能再改變,即它是一個常量。對於基本資料型別,final變數必須顯式地初始化;對於引用型別,final變數只能被初始化一次,並且不能指向另一個物件。

修飾方法:將方法宣告為final意味著該方法不能被重寫(Override)。這通常用於確保父類中的方法不被子類修改

修飾類:將類宣告為final意味著該類不能被繼承。這通常用於建立不可繼承的類,如工具類、單例類等。

Java中的Java NIO是什麼?它與Java IO有什麼區別?
Java NIO(New IO)是Java的一個新的輸入/輸出框架,它在Java 1.4中引入,以改進舊的Java IO框架。Java NIO和Java IO之間的主要區別包括:

非阻塞I/O:Java NIO提供了非阻塞I/O操作,允許在沒有資料可用時繼續執行其他任務,而不是等待I/O操作完成。這可以提高程式的效能和響應性。

選擇器(Selector):Java NIO引入了選擇器,它可以監視一個或多個NIO通道(Channel),並當通道準備就緒時(如可讀、可寫等)執行相應的操作。選擇器簡化了I/O操作的管理,使得單個執行緒可以同時處理多個I/O通道。

緩衝區(Buffer):Java NIO使用緩衝區(Buffer)來儲存資料,而不是直接使用位元組陣列。緩衝區提供了一種靈活且高效的方式來處理資料,它支援鏈式操作,可以方便地進行資料的讀寫和轉換。

Java中的垃圾回收機制是怎樣的?
Java中的垃圾回收機制是自動記憶體管理的一部分,負責自動回收不再使用的物件所佔用的記憶體。垃圾回收器會定期掃描堆記憶體中的物件,找出那些不再被引用的物件,即垃圾物件,然後釋放它們佔用的記憶體。Java中的垃圾回收機制採用了標記-清除、複製、標記-整理等演算法來實現自動記憶體管理。開發者可以透過System.gc()方法建議JVM進行垃圾回收,但具體何時進行垃圾回收仍然由JVM決定。

Java中的序列化是什麼?如何實現物件的序列化?
Java中的序列化是指將物件的狀態轉換為可以儲存或傳輸的形式(如位元組流),以便稍後可以完全恢復物件的狀態。要實現物件的序列化,需要讓該類實現java.io.Serializable介面,該介面是一個標記介面,沒有定義任何方法。然後,可以使用ObjectOutputStream類將物件序列化為位元組流,或者使用ObjectInputStream類從位元組流中反序列化物件。需要注意的是,序列化的物件和其所有屬性都必須是可序列化的,否則會導致序列化失敗。

Java中的靜態變數和例項變數有什麼區別?
靜態變數(Static Variable)和例項變數(Instance Variable)是Java中兩種不同型別的變數,它們之間有幾個主要的區別:

生命週期:靜態變數的生命週期與程式的執行期相同,它們在程式啟動時建立,在程式結束時銷燬。而例項變數的生命週期與物件的存在時間相關,當物件被建立時例項變數被建立,當物件被銷燬時例項變數被銷燬。

儲存位置:靜態變數儲存在Java的記憶體中的方法區,而例項變數儲存在堆記憶體中。

訪問方式:靜態變數可以透過類名直接訪問(需要透過類載入器載入類),而例項變數必須透過類的例項物件訪問。

共享性:靜態變數是類級別的變數,屬於類所有例項共享,即類的所有例項訪問的都是同一個靜態變數。而例項變數是物件級別的變數,每個例項都有自己的例項變數副本,互不影響。

Java中的異常處理機制是怎樣的?
Java中的異常處理機制採用了try-catch-finally結構。當程式執行到try塊中的程式碼時,如果發生異常,則程式會立即跳出當前的執行流程,轉而執行catch塊中的異常處理如果沒有任何catch塊能夠處理該異常,則異常會被傳遞給上層呼叫者,直到被捕獲或導致程式終止。finally塊是可選的,它包含的程式碼無論是否發生異常都會執行。通常用於釋放資源、關閉流等操作。除了try-catch-finally結構外,Java還提供了throw和throws關鍵字來手動丟擲異常和宣告可能丟擲異常的方法。

Java中的集合框架是怎樣的?它包含哪些主要介面和實現類?
Java中的集合框架是一組用於儲存和操作物件的類和介面。它提供了許多有用的資料結構和演算法,使開發者能夠方便地處理資料。Java集合框架主要包含兩個介面:Collection和Map。Collection介面代表了一組物件,它的主要實現類有List、Set等;Map介面代表了一組鍵值對對映,它的主要實現類有HashMap、TreeMap等。除此之外,集合框架還包含了許多其他的介面和實現類,如Queue、Deque、ListIterator等。這些介面和實現類提供了豐富的功能,如新增、刪除、查詢、遍歷等操作,使開發者能夠高效地處理資料。

解釋一下Java中的泛型(Generics)是什麼,以及它們的主要用途。
Java中的泛型(Generics)是JDK 5引入的一個新特性,它允許在定義類、介面和方法時使用型別引數泛型的主要目的是提高程式碼的重用性、型別安全和減少執行時型別轉換的錯誤。透過使用泛型,你可以在編譯時捕獲許多型別錯誤,而不是等到執行時才出現。

Java泛型的主要用途包括:

集合框架中的型別安全:透過使用泛型,你可以確保集合框架中的元素型別正確,避免了執行時型別轉換

自定義泛型類、介面和方法:你可以定義自己的泛型類、介面和方法,使得這些程式碼可以適應多種型別。

型別擦除:雖然Java泛型在編譯時提供了型別檢查,但執行時並不保留泛型資訊。這是Java泛型的一個關鍵特性,稱為型別擦除。

泛型約束:Java 8引入了型別萬用字元和泛型約束(如extends和super關鍵字),這使得泛型的使用更加靈活和強大。

解釋一下Java中的反射(Reflection)是什麼,以及它的主要用途。
Java中的反射(Reflection)是一種強大的機制,它允許程式在執行時檢查類、介面、欄位和方法的資訊,並且能夠建立和操作物件。透過反射,你可以獲取類的所有公共和私有成員,並可以呼叫任意方法。

反射的主要用途包括:

執行時動態載入類:透過反射,你可以動態地載入類,這在許多框架和庫中都非常有用,如Spring、Hibernate等。

訪問私有欄位和方法:儘管這通常不是一個好的做法,但在某些情況下,你可能需要訪問或修改類的私有成員。反射允許你這樣做。

實現框架和工具庫:許多高階框架和工具庫,如ORM框架、序列化/反序列化庫等,都使用了反射技術。

除錯和測試:反射對於除錯和測試也非常有用,因為它允許你在執行時檢查類的結構和行為。

Java中的執行緒生命週期是怎樣的?解釋一下各個狀態。
Java中的執行緒生命週期包括以下幾個狀態:

新建(New):執行緒已經被建立,但尚未啟動。

就緒(Runnable):執行緒已經啟動,正在等待作業系統分配CPU時間片以執行其程式碼。

阻塞(Blocked):執行緒正在等待某個監視器鎖(monitor lock),以便進入同步方法或同步塊。

等待(Waiting):執行緒無限期地等待另一個執行緒執行特定的操作。通常,這是透過呼叫Object類的wait()方法來實現的。

超時等待(Timed Waiting):執行緒在指定的時間內等待另一個執行緒執行特定的操作。這通常是透過呼叫Object類的wait(long timeout)、Thread.sleep(long millis)或LockSupport.parkNanos(long nanos)等方法來實現的。

終止(Terminated):執行緒已經執行完畢,或者因為異常而退出。

執行緒在生命週期中可能會在這幾個狀態之間轉換。例如,一個執行緒在啟動後會從新建狀態轉變為就緒狀態,然後在執行過程中可能會因為等待鎖而進入阻塞狀態,或者因為呼叫等待方法而進入等待或超時等待狀態,最後在執行完畢後進入終止狀態。

解釋一下Java中的併發控制,以及常用的併發工具類。
Java中的併發控制是指多個執行緒同時訪問共享資源時,透過某種手段來保證資源的正確性和安全性。Java提供了多種併發控制機制,如同步(Synchronization)、鎖(Lock)、訊號量(Semaphore)、計數器(Counter)等。

常用的Java併發工具類包括:

synchronized關鍵字:用於實現同步方法或同步塊,以保證多個執行緒對共享資源的正確

ReentrantLock類:可重入鎖,提供了比synchronized更靈活的鎖定機制。

Condition介面:條件變數,用於支援在併發程式設計中的執行緒同步。

Semaphore類:訊號量,用於控制對共享資源的訪問數量。

CountDownLatch類:計數器,用於等待一組執行緒完成操作。

CyclicBarrier類:迴圈柵欄,用於讓一組執行緒相互等待,直到所有執行緒都到達某個屏障點。

Exchanger類:交換器,用於兩個執行緒交換資料。

BlockingQueue介面:阻塞佇列,用於支援執行緒間的資料交換。

解釋一下Java中的多執行緒通訊。
Java中的多執行緒通訊主要涉及到執行緒間的協作和互動。Java提供了幾種機制來實現執行緒間的通訊,包括使用wait()、notify()和notifyAll()方法,以及使用java.util.concurrent包中的工具類,如BlockingQueue和CountDownLatch等。

wait()、notify()和notifyAll()方法:這些方法都是java.lang.Object類的方法,可以在同步塊或同步方法中使用。wait()方法使當前執行緒等待,直到其他執行緒呼叫該物件的notify()或notifyAll()方法。notify()方法喚醒在此物件監視器上等待的單個執行緒,而notifyAll()方法喚醒所有在此物件監視器上等待的執行緒。

BlockingQueue:BlockingQueue是一個支援執行緒間安全通訊的佇列。它提供了put()和take()等方法,這些方法在佇列為空或滿時會阻塞呼叫執行緒。因此,可以使用BlockingQueue在生產者和消費者執行緒之間傳遞資料,從而實現執行緒間的協作。

CountDownLatch:CountDownLatch是一個同步工具類,它允許一個或多個執行緒等待其他執行緒完成操作。CountDownLatch的建構函式接受一個計數值,當計數值遞減到零時,在CountDownLatch上等待的執行緒將被喚醒

Java中的volatile關鍵字有什麼作用?
volatile是Java中的一個關鍵字,它用於宣告變數。當一個變數被宣告為volatile時,這意味著這個變數的值可能會被多個執行緒同時訪問和修改,因此係統會保證每次讀取這個變數時都會直接從主記憶體中讀取,而不是從執行緒的本地快取中讀取。同時,當對這個變數進行修改時,也會立即同步到主記憶體中,而不是僅僅更新執行緒的本地快取。

volatile關鍵字的主要作用是確保變數的可見性和有序性,但並不能保證原子性。它通常用於確保一個執行緒對共享變數的修改對其他執行緒是可見的,以及禁止指令重排最佳化。

Java中的記憶體模型是怎樣的?
Java記憶體模型(Java Memory Model, JMM)定義了Java程式中變數的訪問規則,以及在多執行緒環境下這些變數如何被共享和同步。JMM規定了所有的變數都儲存在主記憶體中,每個執行緒都有自己的本地記憶體或工作記憶體,執行緒對變數的所有操作(讀取和寫入)都必須在自己的工作記憶體中進行,而不能直接對主記憶體中的變數進行操作。執行緒之間共享變數時,需要將變數從主記憶體複製到自己的工作記憶體,然後對自己的本地記憶體中的副本進行操作,最後將更新後的值重新整理回主

JMM透過一系列的規則來確保記憶體操作的原子性、可見性和有序性,從而保證了多執行緒程式的正確性。

什麼是Java的記憶體洩露?如何避免?
記憶體洩露是指程式中動態分配的記憶體沒有得到及時釋放,導致可用記憶體逐漸減少,最終可能會導致程式崩潰。在Java中,記憶體洩露通常發生在長生命週期的物件持有短生命週期物件的引用,導致短生命週期物件無法被垃圾回收器回收。

避免Java記憶體洩露的一些常見做法包括:

及時釋放不再使用的物件引用,避免長生命週期物件持有短生命週期物件的引用。

儘量避免在靜態變數中儲存大量的引用,因為靜態變數的生命週期與程式的執行期相同,容易導致記憶體洩露。

使用弱引用(WeakReference)或軟引用(SoftReference)來引用物件,這樣當系統記憶體不足時,這些物件可以被垃圾回收器回收。

使用工具類如VisualVM、MAT(Memory Analyzer Tool)等來檢測和分析記憶體洩露問題。

Java併發程式設計面試題
解釋什麼是併發和並行?
併發(Concurrency)指的是多個任務在同一時間段內交替執行,共享處理器資源。這通常是透過時間分片的方式實現的,即作業系統將處理器的時間分配給不同的任務,使得每個任務都能夠在短時間內得到執行。

並行(Parallelism)則是指多個任務在同一時間段內同時執行,每個任務都在獨立的處理器核心上執行。這要求有多個處理器核心可用,以實現真正的同時執行。

什麼是執行緒?執行緒和程序有什麼區別?
執行緒是程式執行流的最小單元,它是程序的一個實體,是CPU排程和分派的基本單位。執行緒自己不擁有系統資源,只擁有一點在執行中必不可少的資源(如程式計數器,一組暫存器和棧),但它可與同屬一個程序的其他的執行緒共享程序所擁有的全部資源。

程序是計算機中的程式關於某資料集合上的一次執行活動,是系統進行資源分配和排程的基本單位,是作業系統結構的基礎。程序和執行緒的主要區別在於,程序是資源分配的最小單位,而執行緒是CPU排程的最小單位。一個程序可以包含多個執行緒,但每個執行緒只能屬於一個程序。

Java中的執行緒有幾種建立方式?
在Java中,建立執行緒主要有三種方式

繼承Thread類:可以透過繼承Thread類並重寫其run()方法來建立執行緒。

實現Runnable介面:實現Runnable介面的run()方法,然後將其例項作為引數傳遞給Thread類的構造器來建立執行緒。這種方式比繼承Thread類更為靈活,因為Java不支援多重繼承。

實現Callable介面:與Runnable介面類似,Callable介面也用來定義執行緒的任務,但它可以返回一個結果,並且可以丟擲異常。Callable介面需要使用FutureTask類來建立和啟動執行緒。

什麼是執行緒的生命週期?
執行緒的生命週期包括以下幾個狀態:

新建(NEW):執行緒被建立但尚未啟動。

可執行(RUNNABLE):執行緒正在Java虛擬機器中執行。

阻塞(BLOCKED):執行緒被阻塞,等待監視器鎖,以進入同步塊/方法。

等待(WAITING):執行緒無限期地等待另一個執行緒執行特定的(喚醒)動作。

超時等待(TIMED_WAITING):執行緒在指定的時間內等待另一個執行緒執行特定的(喚醒)動作。

終止(TERMINATED):執行緒的run()方法執行結束,執行緒退出。

解釋一下Java中的執行緒同步。
執行緒同步是指多個執行緒透過協作來完成一項任務,以保證執行緒安全。Java提供了多種機制來實現執行緒同步,包括使用synchronized關鍵字、wait()和notify()方法、Lock介面及其實現類(如ReentrantLock)等。

synchronized關鍵字可以用來修飾方法或程式碼塊,以實現對共享資源的互斥訪問。當一個執行緒進入synchronized程式碼塊時,它會自動獲取相應的鎖,其他試圖進入該程式碼塊的執行緒將被阻塞,直到鎖被釋放。

wait()和notify()方法是Object類的方法,用於執行緒間的通訊和協作。wait()方法使當前執行緒等待,直到其他執行緒呼叫該物件的notify()或notifyAll()方法。notify()方法喚醒在此物件監視器上等待的單個執行緒,而notifyAll()方法喚醒所有在此物件監視器上等待的執行緒

什麼是死鎖?如何避免?
死鎖是指兩個或更多的執行緒在等待一個資源時,由於競爭條件和資源順序的不當使用,導致它們互相等待對方釋放資源,從而無法繼續執行。例如,執行緒A持有資源1並請求資源2,而執行緒B持有資源2並請求資源1,這時就會發生死鎖。

避免死鎖的方法包括:

避免迴圈等待:透過為資源分配唯一的編號,並確保執行緒總是按照編號的順序請求資源。

避免持有並等待:如果一個執行緒請求一個新的資源,而它已經持有了一些資源,那麼它必須首先釋放它持有的所有資源

使用超時機制:執行緒在請求資源時設定一個超時時間,如果在這個時間內沒有得到資源,則放棄對該資源的請求。

什麼是活鎖?如何避免?
活鎖是一種特殊的並發現象,發生在多個執行緒或程序競爭資源時。與死鎖不同,活鎖中的任務或執行者沒有被阻塞,而是由於某些條件沒有滿足,導致它們不斷重複嘗試、失敗、雖然活鎖中的實體在不斷改變狀態,但由於競爭條件或資源爭用的不當策略,它們無法成功完成所需的任務。

活鎖可以視為一種特殊的“飢餓”現象,因為儘管資源是可用的,但由於爭用策略不當,程序或執行緒無法訪問這些資源以完成其工作。

為了避免活鎖,可以採取以下幾種簡單方法:

引入隨機性:當多個執行緒或程序試圖爭用同一資源時,透過引入隨機性來打破競爭的迴圈。例如,讓執行緒或程序在等待資源時隨機休眠一段時間後再嘗試獲取資源。這樣可以打亂它們的執行順序,減少競爭的機率。

使用超時機制:為嘗試獲取資源的操作設定一個超時時間。如果執行緒或程序在規定時間內無法獲取到資源,就放棄當前的操作,並釋放已佔用的資源。這可以避免執行緒或程序一直等待資源而無法繼續執行,從而減少活鎖的可能性。

資源讓渡:當一個執行緒或程序發現自己無法獲取所需的資源時,可以主動釋放已佔用的資源,並重新嘗試獲取資源。這可以避免某些執行緒或程序一直爭搶同一資源而無法取得進展的情況。

順序化資源獲取:對於可能導致活鎖的資源競爭情況,可以使用全域性的固定順序來獲取這些資源。透過按照預定的順序獲取資源,可以避免多個執行緒或程序陷入無限迴圈的競爭中。

避免無用的重試:在處理活鎖問題時,應該避免執行緒或程序過於頻繁地重試獲取資源。可以在每次重試之前先檢查資源的可用性,只有當資源確實可用時才進行重試。

透過採取上述方法,可以有效降低活鎖的發生機率,提高多執行緒程式或併發系統的執行效率。然而,在某些複雜的併發場景中,可能還需要結合其他高階併發控制策略來解決活鎖問題。

解釋一下Java中的執行緒池及其優點。
執行緒池是一種執行緒管理技術,它預先建立和管理一定數量的執行緒,當有任務需要執行時,執行緒池會分配一個執行緒來執行任務。Java中的執行緒池主要由java.util.concurrent.ExecutorService介面和其實現類(如ThreadPoolExecutor)提供。

執行緒池的優點包括:

降低資源消耗:透過複用已建立的執行緒,避免頻繁地建立和銷燬執行緒,從而減少系統資源的消耗。

提高響應速度:執行緒池在任務到達時,可以立即分配執行緒執行任務,從而提高了系統的響應速度。

提高系統吞吐量:執行緒池可以併發地執行多個任務,從而提高了系統的吞吐量。

提供執行緒管理功能:執行緒池提供了對執行緒的統一管理,包括執行緒的建立、銷燬、狀態監控等,使得執行緒的管理更加便捷和高效。

解釋一下Java中的阻塞佇列及其作用。
阻塞佇列是一種特殊的佇列,它在佇列為空時,從佇列中獲取元素的執行緒會被阻塞,直到佇列中有新的元素加入;當佇列已滿時,嘗試向佇列中新增元素的執行緒也會被阻塞,直到佇列中有空閒位置。Java中的java.util.concurrent包提供了多種阻塞佇列的實現,如ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue等。

阻塞佇列在併發程式設計中起到了重要的作用,它們常常被用來作為生產者-消費者模式中的緩衝區,以實現生產者和消費者之間的解耦和協同工作。透過使用阻塞佇列,生產者可以在佇列滿時阻塞等待,消費者可以在佇列空時阻塞等待,從而避免了生產者和消費者之間的競態條件和資源

什麼是Java中的volatile關鍵字?它在併發程式設計中的作用是什麼?
volatile是Java中的一個關鍵字,它用於宣告變數。當一個變數被宣告為volatile時,這意味著這個變數的值可能會被多個執行緒同時訪問和修改,因此係統會保證每次讀取這個變數時都會直接從主記憶體中讀取,而不是從執行緒的本地快取中讀取。同時,當對這個變數進行修改時,也會立即同步到主記憶體中,而不是僅僅更新執行緒的本地快取。

在併發程式設計中,volatile關鍵字的主要作用是確保變數的可見性和有序性,但並不能保證原子性。它通常用於確保一個執行緒對共享變數的修改對其他執行緒是可見的,以及禁止指令重排最佳化。但是,需要注意的是,volatile並不能替代鎖機制,因為它不能解決複合操作的原子性問題。

以上只是關於Java併發程式設計面試題的一部分,實際上併發程式設計是一個深入且廣泛的領域,涉及到很多其他的概念和技術,如鎖機制、原子變數、併發容器、執行緒池的配置和最佳化等。因此,建議深入學習併發程式設計的相關知識,以更好地應對各種面試挑戰。

解釋一下Java中的執行緒狀態及其轉換?
Java中的執行緒狀態包括:

NEW:執行緒被建立但尚未啟動。

RUNNABLE:執行緒正在Java虛擬機器中執行。

BLOCKED:執行緒被阻塞,等待監視器鎖(通常是因為進入同步程式碼塊或方法)。

WAITING:執行緒無限期地等待另一個執行緒執行特定的操作。

TIMED_WAITING:執行緒在指定的時間內等待另一個執行緒執行特定的操作。

TERMINATED:執行緒已退出,即執行結束。

執行緒的狀態轉換主要依賴於執行緒的執行和同步控制。

解釋一下Java中的執行緒池及其優點。
執行緒池是一種執行緒管理技術,它預先建立和管理一定數量的執行緒,當有任務需要執行時,執行緒池會分配一個執行緒來執行任務。Java中的ExecutorService介面及其實現類(如ThreadPoolExecutor)提供了執行緒池的功能。

執行緒池的優點包括:

降低資源消耗:透過複用已建立的執行緒,減少執行緒建立和銷燬的開銷。

提高響應速度:當有任務到達時,執行緒池可以立即分配執行緒執行任務。

提高系統吞吐量:執行緒池可以併發執行多個任務。

提供執行緒管理功能:執行緒池提供了對執行緒的統一管理,包括執行緒的建立、銷燬、狀態監控等。

Java中有哪些常用的併發集合?
Java中常用的併發集合包括:

ConcurrentHashMap:執行緒安全的HashMap實現。

CopyOnWriteArrayList:執行緒安全的ArrayList實現,適用於讀多寫少的場景。

CopyOnWriteArraySet:執行緒安全的Set實現,基於CopyOnWriteArrayList。

BlockingQueue介面及其實現類(如ArrayBlockingQueue、LinkedBlockingQueue):支援執行緒間同步的佇列,常用於生產者-消費者模式。

Java中的鎖有哪些型別?它們之間有什麼區別?
Java中的鎖主要有兩種型別:

內建鎖(或稱為同步鎖):透過synchronized關鍵字實現,它可以修飾方法或程式碼塊。內建鎖是互斥的,同一時間只能被一個執行緒持有。

顯式鎖(如ReentrantLock):透過java.util.concurrent.locks.Lock介面及其實現類(如ReentrantLock)實現。顯式鎖提供了更靈活的鎖控制,例如可以中斷等待鎖的執行緒,也可以嘗試非阻塞地獲取鎖。

主要區別在於,內建鎖是語言層面的特性,而顯式鎖是Java併發包提供的API。內建鎖不可中斷,而顯式鎖可以。

解釋一下Java的記憶體模型以及Java Memory Model (JMM)的作用。
Java的記憶體模型是一個抽象的概念,描述了Java程式中變數的訪問規則。Java記憶體模型規定了所有的變數都儲存在主記憶體(Main Memory)中,每條執行緒還有自己的工作記憶體(Working Memory),執行緒對變數的操作(讀取和寫入)都在自己的工作記憶體中進行,然後再同步回主記憶體。

Java Memory Model (JMM)的作用是定義並規範了Java程式中各個變數(包括例項欄位、靜態欄位和陣列元素)的訪問規則,以及在多執行緒環境下這些變數的可見性、有序性和原子性。JMM保證了在併發程式設計中,程式的正確性和效能。

當然,以下是關於Java併發程式設計的面試題及答案的繼續內容:

什麼是volatile關鍵字?它在Java中有什麼作用?
volatile是Java中的一個關鍵字,主要用於確保多執行緒環境中變數的可見性。當一個變數被宣告為volatile時,它會保證修改的值會立即被更新到主記憶體,當有其他執行緒需要讀取時,它會去主記憶體中讀取新值。這確保了多執行緒環境下變數的正確性和一致性。

volatile還禁止進行指令重排序,從而確保了操作的順序性。

解釋一下Java中的死鎖以及如何避免?
死鎖是指兩個或更多的執行緒無限期地等待一個資源或彼此等待對方釋放資源,導致所有相關執行緒都無法

避免死鎖的方法包括:

避免巢狀鎖:儘量不在一個執行緒中同時鎖定多個物件,如果確實需要,則確保鎖定的順序一致。

嘗試鎖定:使用Lock介面中的tryLock方法嘗試獲取鎖,如果獲取失敗則執行其他邏輯,避免無限等待。

超時鎖定:設定鎖定的超時時間,避免長時間等待。

使用鎖順序:如果多個執行緒需要請求多個鎖,確保所有執行緒都按照相同的順序請求鎖。

死鎖檢測與恢復:使用專門的工具或庫來檢測死鎖,並在檢測到死鎖時採取措施恢復,例如中斷涉及死鎖的執行緒。

什麼是Java中的CAS操作?它有什麼特點?
CAS(Compare-and-Swap)是一種無鎖機制,用於實現併發程式設計中的原子操作。CAS操作包含三個運算元——記憶體位置(V)、期望的原值(A)和更新值(B)。執行CAS操作時,會將記憶體位置V的值與期望的原值A進行比較,如果相匹配,那麼處理器會自動將該記憶體位置V的值更新為B。如果不匹配,處理器不做任何操作。無論哪種情況,它都會在CAS指令之前返回該位置的值。

CAS操作的特點包括:

原子性:CAS操作是原子的,即在執行過程中不會被其他執行緒干擾。

非阻塞:CAS操作是非阻塞的,它不會使執行緒進入阻塞狀態,從而提高了併發效能。

自旋重試:當CAS操作失敗時,通常會透過迴圈重試來確保操作的最終成功

解釋一下Java中的鎖分段技術。
鎖分段技術是一種提高併發效能的技術,透過將鎖分解為多個鎖,使得多個執行緒可以併發地訪問不同的資料段,從而減少鎖的競爭。在Java中,ConcurrentHashMap就使用了鎖分段技術,它將內部資料分為多個段,每個段都有自己的鎖,當對某個段的資料進行訪問時,只需要獲取該段的鎖,從而減少了鎖的競爭。這種技術可以提高併發訪問的效能,尤其是在高併發場景下。

解釋一下Java中的阻塞佇列(BlockingQueue)及其應用場景。
Java中的BlockingQueue介面是java.util.concurrent包中的一個關鍵元件,它支援在佇列為空時阻塞等待元素可用的執行緒,以及在佇列滿時阻塞等待佇列有空閒空間的執行緒。這種佇列在多執行緒程式設計中非常有用,特別是在生產者-消費者模式中。

應用場景:

生產者-消費者模式:生產者在佇列滿時阻塞,直到消費者消費了元素並釋放空間。同樣,消費者在佇列為空時阻塞,直到生產者生產了新元素。

執行緒池:ThreadPoolExecutor內部使用BlockingQueue作為工作佇列,儲存待執行的任務。

非同步處理:當需要在不同的執行緒間傳遞資料時,可以使用BlockingQueue實現非同步處理。

什麼是Java中的執行緒區域性變數(ThreadLocal)?為什麼它是執行緒安全的?
ThreadLocal是Java提供的一個執行緒區域性變數的類。每個執行緒都會持有一個ThreadLocal變數的副本,因此ThreadLocal可以在不同的執行緒間隔離資料。

為什麼它是執行緒安全的?

執行緒隔離:每個執行緒都持有自己的ThreadLocal變數副本,因此不存在多個執行緒訪問同一記憶體位置的問題。

清除機制:ThreadLocal提供了remove()方法來清除執行緒不再需要的變數,從而避免了記憶體洩漏。

解釋一下Java中的公平鎖和非公平鎖的區別。
公平鎖和非公平鎖是Java中兩種不同型別的鎖,它們在獲取鎖時的行為有所不同。

公平鎖:按照執行緒請求鎖的順序來獲取鎖。如果一個執行緒先來請求鎖,那麼它應該比後來的執行緒先獲得鎖。這種鎖保證了等待時間最長的執行緒優先獲得鎖。

非公平鎖:允許後來請求的執行緒跳過等待佇列中的執行緒,直接獲取鎖。這可能導致某些執行緒始終獲取不到鎖,從而造成“飢餓”現象。Java中的ReentrantLock類就支援公平鎖和非公平鎖兩種方式。

什麼是Java中的分段鎖(Segmented Lock)?它與ReentrantLock有何不同?
分段鎖是一種將鎖分解為多個子鎖或段的技術,每個段都有自己的鎖。Java中的ConcurrentHashMap就使用了分段鎖技術。每個段都有自己的鎖,因此多個執行緒可以同時訪問不同的段,從而提高併發效能。

與ReentrantLock相比,分段鎖具有以下不同點:

鎖粒度:ReentrantLock是一個獨佔鎖,它對整個資料結構加鎖,因此鎖粒度較大。而分段鎖將整個資料結構劃分為多個段,每個段都有自己的鎖,因此鎖粒度較小,可以提高併發效能。

併發效能:由於分段鎖允許多個執行緒同時訪問不同的段,因此它在高併發場景下通常具有更好的效能。而ReentrantLock在多個執行緒競爭同一把鎖時,可能會導致執行緒阻塞,降低併發效能。

Java中的執行緒如何停止?
在Java中,不建議使用Thread.stop()方法來停止執行緒,因為它可能導致執行緒不安全地終止,從而產生不可預知相反,應該使用更加安全的方式來停止執行緒,例如:

設定標誌位:線上程的執行邏輯中設定一個標誌位,當需要停止執行緒時,將該標誌位設定為true,執行緒在執行過程中檢查該標誌位,如果為true,則安全地終止執行緒的執行。

使用中斷:透過呼叫執行緒的interrupt()方法來中斷執行緒。執行緒可以檢查中斷狀態(使用Thread.interrupted()或Thread.isInterrupted()方法),並在適當的時候響應中斷請求,安全地終止執行緒的執行。

解釋一下Java中的執行緒狀態及其轉換。
Java中的執行緒狀態可以透過Thread.State列舉來表示,它包括了以下幾種狀態:

NEW:執行緒已建立但尚未啟動。

RUNNABLE:執行緒正在Java虛擬機器中執行。

BLOCKED:執行緒被阻塞,等待監視器鎖(通常是因為進入了同步塊/方法)。

WAITING:執行緒無限期等待另一個執行緒執行特定的(喚醒)動作。

TIMED_WAITING:執行緒在指定的時間內等待另一個執行緒執行特定的(喚醒)動作。

TERMINATED:執行緒已退出,即執行結束。

執行緒狀態之間的轉換通常是由執行緒的生命週期和執行緒之間的互動決定的。例如,一個執行緒從NEW狀態轉換到RUNNABLE狀態是透過呼叫start()方法實現的;當執行緒嘗試獲取一個已被其他執行緒持有的監視器鎖時,它可能會從RUNNABLE狀態轉換到BLOCKED狀態;當執行緒呼叫Object.wait()方法時,它會從RUNNABLE狀態轉換到WAITING或TIMED_WAITING狀態;當執行緒完成其執行後,它會從RUNNABLE狀態轉換到TERMINATED狀態。

Java中的執行緒通訊主要有哪幾種方式?
Java中的執行緒通訊主要有以下幾種方式:

共享記憶體:透過共享變數或物件,執行緒之間可以傳遞資料當多個執行緒需要訪問共享資源時,需要使用同步機制(如synchronized關鍵字或Lock介面)來確保執行緒安全。

wait/notify機制:Object類提供了wait()、notify()和notifyAll()方法來支援執行緒間的通訊。一個執行緒可以呼叫物件的wait()方法進入等待狀態,直到其他執行緒呼叫該物件的notify()或notifyAll()方法喚醒它。

Condition介面:java.util.concurrent.locks包中的Condition介面提供了更靈活和強大的執行緒同步機制。它允許執行緒在特定的條件上等待和喚醒,而不是在整個物件上。

BlockingQueue:BlockingQueue是一個支援執行緒間通訊的佇列,它允許執行緒在佇列為空時等待,直到其他執行緒向佇列同樣,當佇列滿時,嘗試插入元素的執行緒會等待,直到其他執行緒從佇列中移除元素。

Semaphore:Semaphore是一個計數訊號量,它可以用來控制對共享資源的訪問。執行緒可以透過獲取和釋放訊號量來請求和釋放資源,從而實現執行緒間的同步和通訊。

解釋一下Java中的活鎖(Livelock)及其產生的原因。
活鎖(Livelock)是一種特殊的並發現象,它發生在兩個或更多的程序或執行緒反覆嘗試執行某項任務,但由於競爭條件或不當的同步策略,它們永遠都無法成功完成該任務。

活鎖產生的主要原因包括:

不當的同步策略:當執行緒間存在錯誤的同步邏輯時,可能會導致它們陷入無休止的迴圈中,無法達成有效的合作。

資源爭用:如果多個執行緒不斷競爭同一資源,並且每次競爭都以失敗告終,那麼它們可能會陷入活鎖狀態。

死迴圈中的執行緒排程:如果執行緒在沒有退出條件的情況下不斷嘗試獲取資源或執行某項任務,並且由於執行緒排程策略的原因,這些執行緒始終無法成功,那麼它們也可能陷入活鎖狀態。

為了避免活鎖,開發者應該仔細設計執行緒間的同步策略和協作機制,確保執行緒能夠在適當的時候獲得資源併成功完成任務;同時,也要考慮使用合適的執行緒排程策略和避免過度的執行緒競爭。

什麼是Java中的鎖順序死鎖(Lock Ordering Deadlock)?
鎖順序死鎖是指多個執行緒試圖以不同的順序獲取鎖,從而導致一個執行緒在等待其他執行緒釋放鎖,而後者又在等待前者釋放鎖的情況。這種迴圈等待導致所有執行緒都無法繼續執行,從而引發死鎖。

預防鎖順序死鎖的方法:

固定鎖順序:確保所有執行緒都按照相同的順序請求鎖。這樣,即使存在多個執行緒,也不會形成迴圈等待的情況。

避免巢狀鎖:儘量減少在一個執行緒中持有多個鎖的時間。如果必須持有多個鎖,嘗試將它們一次性全部獲取,而不是逐步獲取。

設定鎖超時:為鎖操作設定超時時間,以便在無法獲得鎖時執行緒能夠及時放棄,從而避免死鎖

使用嘗試鎖:java.util.concurrent.locks.Lock介面提供了tryLock()方法,該方法嘗試獲取鎖,如果鎖不可用,則立即返回而不阻塞。透過使用嘗試鎖,執行緒可以在無法獲取鎖時選擇其他操作,從而避免死鎖。

解釋一下Java中的可重入鎖(Reentrant Lock)及其作用。
可重入鎖(Reentrant Lock)是一種允許多個執行緒對同一資源進行重複加鎖的鎖機制。與內建鎖(即synchronized)不同,可重入鎖不會被一個已經獲取鎖的執行緒重複獲取而阻塞,而是允許該執行緒多次獲取鎖。

可重入鎖的作用:

解決死鎖問題:在某些情況下,執行緒可能需要多次獲取同一把鎖。如果使用不可重入的鎖,則可能導致死鎖。而可重入鎖允許執行緒多次獲取鎖,從而避免了死鎖問題。

提高效能:可重入鎖通常比內建鎖具有更高的效能。這是因為內建鎖在獲取鎖和釋放鎖時需要進行狀態切換,而可重入鎖在多次獲取鎖時不需要進行額外的狀態切換。

提供更多的靈活性:可重入鎖提供了更多的靈活性,比如支援公平鎖和非公平鎖、支援中斷等。這使得開發者可以根據具體的需求選擇適合的鎖策略。

Java中的ReentrantLock類就是一個典型的可重入鎖實現。使用ReentrantLock時,開發者需要手動獲取和釋放鎖,而不是像使用synchronized那樣由JVM自動管理。這要求開發者在編寫程式碼時要格外小心,以確保鎖的正確使用。

解釋一下Java中的樂觀鎖和悲觀鎖。
樂觀鎖和悲觀鎖是兩種不同的併發控制策略,它們在處理併發問題時的態度和方法不同。

樂觀鎖:樂觀鎖假設多個執行緒同時修改同一資料的機率很小,因此在資料處理過程中不會直接鎖定資料。只是在更新資料時,會判斷在此期間有沒有其他執行緒修改過這個資料,有則採取回滾等方式解決,沒有則完成更新。樂觀鎖通常是透過資料版本記錄機制來實現。

悲觀鎖:悲觀鎖則正好相反,它認為多個執行緒同時修改同一資料的機率很高,所以鎖定操作過程中所涉及的資料,避免其他執行緒進行操作。悲觀鎖的實現通常依賴於資料庫的鎖機制。

在Java中,樂觀鎖通常是透過資料版本記錄機制來實現。例如,在資料庫中新增一個版本欄位,每次更新資料時,都會檢查版本欄位的值是否發生變化。如果發生變化,則說明有其他執行緒修改過該資料,此時可以採取回滾等方式解決。而悲觀鎖則可以透過Java的synchronized關鍵字或java.util.concurrent.locks.Lock介面來實現。

這兩種鎖各有優缺點,適用於不同的場景。在實際開發中,需要根據具體的需求和場景選擇合適的鎖策略。

什麼是Java中的自旋鎖(Spinlock),它適用於什麼場景?
自旋鎖(Spinlock)是一種特殊的鎖機制,當執行緒嘗試獲取鎖失敗時,它不會立即進入阻塞狀態,而是會在一個迴圈中不斷嘗試獲取鎖,直到成功為止。這種不斷嘗試獲取鎖的過程被稱為“自旋”。

自旋鎖適用的場景:

短期鎖定:自旋鎖適用於預期鎖定時間較短的場景。由於自旋鎖不會使執行緒進入阻塞狀態,因此在鎖定時間短的情況下,可以避免執行緒切換帶來的開銷,從而提高效能。

鎖競爭激烈:在多個執行緒頻繁競爭同一把鎖的場景下,自旋鎖可能是一個不錯的選擇。由於自旋鎖不會使執行緒進入阻塞狀態,因此可以減少執行緒因等待鎖而阻塞的時間,從而提高系統的併發效能。

避免死鎖:在某些情況下,使用自旋鎖可以避免死鎖的發生。例如,在鎖的順序不固定的場景下,自旋鎖可以防止執行緒在等待鎖時發生迴圈等待,從而避免死鎖的發生。

然而,需要注意的是,自旋鎖並不適用於所有場景。當鎖定時間較長或執行緒數量較多時,自旋鎖可能會導致執行緒持續消耗CPU資源而降低系統效能。因此,在選擇是否使用自旋鎖時,需要根據具體的場景和需求進行評估和權衡。

解釋一下Java中的鎖降級(Lock Downgrading)。
鎖降級是從一個高階別的鎖轉換到一個低階別的鎖的過程。在Java中,這通常涉及到從一個排他鎖(Exclusive Lock)轉換到一個共享鎖(Shared Lock)。排他鎖允許一個執行緒獨佔資源,而共享鎖允許多個執行緒同時訪問資源,但可能以某種方式限制對資源的訪問。

鎖降級的意義:

提高併發效能:透過將高階別的鎖轉換為低階別的鎖,鎖降級可以在保持資料完整性的同時提高系統的併發效能。

靈活性:鎖降級允許開發者根據具體的需求和場景靈活地選擇鎖的型別和級別,以滿足不同的併發控制需求。

實現鎖降級的方法:

使用ReentrantReadWriteLock:Java中的ReentrantReadWriteLock類支援鎖降級。該類允許執行緒首先獲取一個寫鎖(排他鎖),然後在其內部邏輯中根據需要降級為讀鎖(共享鎖)。

手動管理鎖:在某些情況下,開發者可能需要手動管理鎖來實現鎖降級。例如,可以首先獲取一個排他鎖,然後在釋放排他鎖之前獲取一個共享鎖。這需要開發者仔細設計和編寫程式碼,以確保正確的鎖管理和避免死鎖等問題。

需要注意的是,鎖降級可能會增加系統的複雜性,並可能引入新的併發問題。因此,在使用鎖降級時,開發者需要仔細評估其需求和場景,並遵循正確的鎖管理原則來避免潛在的問題。

什麼是Java中的阻塞佇列(Blocking Queue)?它在多執行緒程式設計中的作用是什麼?
Java中的阻塞佇列(Blocking Queue)是一種支援兩個附加操作的佇列:在佇列為空時,獲取元素的執行緒將會阻塞,直到有元素可獲取;當佇列已滿時,嘗試新增元素的執行緒將會阻塞,直到佇列有空閒空間。

阻塞佇列在多執行緒程式設計中的作用:

解耦:在生產者消費者模式中,生產者和消費者不需要知道彼此的存在,只需要透過阻塞佇列進行互動,這樣可以降低程式各模組之間的耦合度。

緩衝:當生產者和消費者的處理能力存在差異時,阻塞佇列可以起到緩衝的作用,使得生產者可以在消費者處理能力不足時,先將資料放入佇列中,等消費者處理能力恢復後再從佇列中取出資料進行處理。

執行緒安全:阻塞佇列本身是執行緒安全的,可以在多執行緒環境下安全地使用。

支援公平和非公平訪問:根據具體的實現類,阻塞佇列可以支援公平訪問(按照執行緒請求的順序進行服務)或非公平訪問(不保證執行緒請求的服務順序)。

Java提供了多種阻塞佇列的實現,如ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue、DelayQueue等,它們適用於不同的場景和需求。

解釋一下Java中的執行緒池(Thread Pool)及其優點。
執行緒池是Java中用於管理執行緒生命週期的一種技術,它建立並維護一組執行緒,允許應用程式重用已經建立的執行緒,而不是每次需要執行新任務時都建立新執行緒

執行緒池的優點:

減少建立和銷燬執行緒的開銷:執行緒的建立和銷燬都需要一定的系統資源,頻繁地建立和銷燬執行緒會消耗大量的系統資源。執行緒池透過重用已經建立的執行緒,減少了執行緒建立和銷燬的開銷。

提高響應速度:當任務到達時,執行緒池可以立即分配執行緒執行任務,而不需要等待執行緒的建立。這提高了系統的響應速度。

提高系統的吞吐量:執行緒池可以根據系統的負載情況,動態地調整執行緒池的大小,從而提高了系統的吞吐量。

更好的資源管理:執行緒池允許開發者設定核心執行緒數、最大執行緒數、佇列容量等引數,從而更好地管理系統的資源。

Java中提供了多種執行緒池的實現,如FixedThreadPool、CachedThreadPool、ScheduledThreadPool等,它們適用於不同的場景和需求

使用執行緒池可以有效地管理執行緒,提高系統的效能和穩定性。然而,也需要注意合理地配置執行緒池的引數,避免資源過度消耗或浪費。

Java多執行緒面試題
什麼是Java中的多執行緒?為什麼使用多執行緒?
Java中的多執行緒指的是在同一時間內執行多個任務或操作的能力。使用多執行緒的主要原因包括:

提高效能:多執行緒可以利用多核CPU的並行處理能力,從而提高程式的執行效率。

簡化程式設計:某些任務可以獨立地執行,而不需要等待其他任務完成。使用多執行緒可以將這些任務分解為獨立的單元,簡化程式設計。

資源共享:多個執行緒可以共享程序中的記憶體空間和系統資源,減少了資源的浪費。

如何在Java中建立執行緒?
在Java中,可以透過以下兩種方式建立執行緒:

繼承Thread類:建立一個類繼承自Thread類,並重寫run()方法。然後建立該類的例項並呼叫start()方法來啟動執行緒。

class MyThread extends Thread {
@Override
public void run() {
// 執行緒執行的程式碼
}
}

MyThread thread = new MyThread();
thread.start();
實現Runnable介面:建立一個類實現Runnable介面,並實現run()方法。然後建立該類的例項,並將其作為引數傳遞給Thread類的構造方法,最後呼叫start()方法來啟動執行緒。

class MyRunnable implements Runnable {
@Override
public void run() {
// 執行緒執行的程式碼
}
}

MyRunnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
什麼是執行緒的生命週期?請描述一下。
執行緒的生命週期包括以下狀態:

新建(New):執行緒被建立但尚未啟動。

就緒(Runnable):執行緒已經啟動,正在等待被分配到CPU執行。

阻塞(Blocked):執行緒被阻塞,等待某個監視器鎖(monitor lock)以進入同步塊/方法。

等待(Waiting):執行緒無限期地等待另一個執行緒執行特定的操作。

超時等待(Timed Waiting):執行緒在指定的時間內等待另一個執行緒執行特定的操作。

終止(Terminated):執行緒已退出,即run()方法已執行完畢。

什麼是執行緒的優先順序?如何設定執行緒的優先順序?
執行緒的優先順序是執行緒排程中的一個重要因素,它決定了執行緒的執行順序。Java中的執行緒優先順序是一個整數,其值範圍從1(最低優先順序)到10(最高優先順序)。

可以透過setPriority(int)方法來設定執行緒的優先順序。例如:

Thread thread = new Thread();
thread.setPriority(Thread.MAX_PRIORITY); // 設定最高優先順序
什麼是執行緒同步?為什麼需要執行緒同步?
執行緒同步是指控制多個執行緒對共享資源的訪問,以避免資料不一致或其他執行緒安全問題。

需要執行緒同步的主要原因有:

保證資料完整性:避免多個執行緒同時訪問和修改共享資源,導致資料不一致。

避免競態條件:確保執行緒之間的操作順序符合預期,防止產生不可預測的結果。

Java中有哪些實現執行緒同步的方法?
Java中實現執行緒同步的方法主要包括:

synchronized關鍵字:用於修飾方法或程式碼塊,保證同一時刻只有一個執行緒可以執行被修飾的程式碼。

public synchronized void synchronizedMethod() {
// 同步方法
}

public void anotherMethod() {
synchronized (this) {
// 同步程式碼塊
}
}
wait()和notify()方法:wait()方法使當前執行緒等待,直到其他執行緒呼叫該物件的notify()或notifyAll()方法。notify()方法喚醒一個等待該物件的執行緒,而notifyAll()方法喚醒所有等待該物件的執行緒。

Lock介面和Condition介面:Java的java.util.concurrent.locks包提供了更靈活的執行緒同步機制,包括ReentrantLock類和Condition介面。

Java中的執行緒通訊主要有哪些方式?
Java中的執行緒通訊主要透過以下方式實現:

wait() 和 notify() / notifyAll() 方法:這些方法通常與synchronized關鍵字一起使用,在一個物件的監視器(monitor)上等待或喚醒其他執行緒。wait()方法使當前執行緒等待,直到其他執行緒呼叫同一個物件的notify()或notifyAll()方法。

使用volatile關鍵字:volatile關鍵字可以確保多個執行緒對共享變數的可見性,但它並不保證原子性。它常用於標記一個變數,以便其他執行緒可以看到這個變數的最新值。

使用阻塞佇列(BlockingQueue):Java的java.util.concurrent包中的BlockingQueue介面提供了一種執行緒安全的佇列,可以線上程之間傳遞資料。生產者執行緒將元素新增到佇列中,消費者執行緒從佇列中移除元素。如果佇列為空,消費者執行緒會阻塞;如果佇列已滿,生產者執行緒會阻塞。

使用Condition介面:java.util.concurrent.locks包中的Condition介面允許更細粒度的執行緒間協調。與wait()和notify()不同,Condition允許多個等待集(wait-set),因此執行緒可以根據需要在不同的等待集中等待。

什麼是守護執行緒(Daemon Thread)?它與使用者執行緒(User Thread)有什麼不同?
守護執行緒(Daemon Thread)是在後臺執行並支援其他執行緒(使用者執行緒)的執行緒。當所有的使用者執行緒都結束執行時,守護執行緒會隨JVM一起退出,即使守護執行緒還在執行中。

使用者執行緒(User Thread)是程式中直接執行的執行緒,它們負責執行程式的主要功能。只有當所有的使用者執行緒都結束時,程式才會退出。

守護執行緒通常用於執行後臺任務,如垃圾收集、記憶體管理等,而使用者執行緒則用於執行程式的主要功能。

如何在Java中建立守護執行緒?
在Java中,可以透過呼叫執行緒的setDaemon(true)方法將其設定為守護執行緒。需要注意的是,必須在啟動執行緒之前設定守護狀態,否則將丟擲IllegalThreadStateException。

Thread daemonThread = new Thread(() -> {
// 守護執行緒執行的程式碼
});
daemonThread.setDaemon(true); // 設定為守護執行緒
daemonThread.start(); // 啟動執行緒
請注意,主執行緒(即呼叫main方法的執行緒)預設不是守護執行緒,如果主執行緒結束,即使還有守護執行緒在執行,JVM也會退出。

什麼是執行緒池(Thread Pool)?為什麼使用執行緒池?
執行緒池是一個用於管理執行緒集合的框架,它負責執行緒的建立、使用、管理和銷燬。執行緒池可以減少建立和銷燬執行緒的開銷,提高系統響應速度,並且能夠有效地控制系統中併發執行緒的數量。

使用執行緒池的主要原因包括:

降低資源消耗:透過複用已建立的執行緒,避免頻繁地建立和銷燬執行緒,從而減少系統資源的

提高響應速度:執行緒池能夠在任務到達時立即執行,減少任務等待時間,提高系統的響應速度。

提高系統吞吐量:執行緒池能夠同時處理多個任務,提高系統的處理能力。

更好地控制併發數量:透過執行緒池,我們可以更好地控制併發執行緒的數量,避免過多執行緒導致系統資源耗盡。

Java中如何建立執行緒池?
在Java中,可以透過java.util.concurrent包中的Executors類來建立執行緒池。Executors類提供了多種建立執行緒池的方法,如newFixedThreadPool、newCachedThreadPool、newSingleThreadExecutor

例如,建立一個固定大小的執行緒池可以使用newFixedThreadPool方法:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolExample {
public static void main(String[] args) {
// 建立一個固定大小的執行緒池
ExecutorService executor = Executors.newFixedThreadPool(5);

// 提交任務到執行緒池
for (int i = 0; i < 10; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("Executing task " + taskId + " in thread " + Thread.currentThread().getName());
});
}

// 關閉執行緒池(注意:這不會立即關閉執行緒池,而是會等待所有任務執行完畢)
executor.shutdown();
while (!executor.isTerminated()) {
// 等待所有任務完成
}
System.out.println("All tasks have been completed.");
}
}
執行緒池中的任務佇列是什麼?有哪些常見的任務佇列?
執行緒池中的任務佇列用於儲存待執行的任務。當執行緒池中的執行緒數量達到最大值時,新提交的任務會被儲存在任務佇列中,等待執行緒池中的執行緒空閒出來執行。

常見的任務佇列有以下幾種:

ArrayBlockingQueue:一個由陣列結構組成的有界佇列。

LinkedBlockingQueue:一個由連結串列結構組成的有界佇列,但預設大小為Integer.MAX_VALUE,可以認為幾乎是無

SynchronousQueue:一個不儲存元素的阻塞佇列,每個插入操作必須等待一個相應的刪除操作,反之亦然。

PriorityBlockingQueue:一個支援優先順序排序的無界阻塞佇列。

執行緒池具體使用哪種任務佇列取決於建立執行緒池時所選的方法以及傳遞給這些方法的引數。

如何正確關閉執行緒池?
關閉執行緒池的正確做法是使用shutdown()或shutdownNow()方法。shutdown()方法會啟動執行緒池的有序關閉,已經提交的任務會繼續執行,但不再接受新任務。當所有任務都執行完畢後,執行緒池關閉。而shutdownNow()方法則試圖停止所有正在執行的任務,暫停處理正在等待的任務,並返回等待執行的任務列表。

通常建議首先呼叫shutdown()方法,等待足夠的時間讓執行緒池處理完已提交的任務,然後再檢查是否執行緒池已經關閉,如果未關閉,再呼叫shutdownNow()方法來關閉執行緒池。

executor.shutdown(); // 禁用新任務的提交
try {
// 等待已提交任務執行完畢(超時等待)
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
executor.shutdownNow(); // 如果超時未關閉,則強制關閉
}
} catch (InterruptedException ie) {
// 如果當前執行緒在等待過程中被中斷,則重新中斷它
executor.shutdownNow();
// 保留中斷狀態
Thread.currentThread().interrupt();
}
Java中的執行緒區域性變數是什麼?
在Java中,執行緒區域性變數(Thread Local Variables)是一種特殊的變數,它們不是共享的,而是與每個執行緒關聯的。這意味著每個執行緒都可以獨立地修改自己的執行緒區域性變數副本,而不會影響到其他執行緒的相應變數。

執行緒區域性變數是透過ThreadLocal類來實現的。ThreadLocal類提供了執行緒本地變數。這些變數與普通變數的區別在於,每個訪問該變數的執行緒都會有該變數的一個獨立初始化副本。ThreadLocal例項通常作為靜態的私有欄位出現在一個類中,用於關聯執行緒和該執行緒儲存的資料。

當一個執行緒首次訪問一個ThreadLocal變數時,它會使用ThreadLocal的initialValue()方法(如果提供了)來初始化該變數的副本。此後,每個執行緒對該變數的後續訪問都將操作這個獨立的副本,而不是訪問其他執行緒的副本。

執行緒區域性變數常用於需要在多個執行緒之間保持獨立狀態的情況,比如資料庫連線、事務上下文、執行緒特定的配置資訊等。它們可以避免在多執行緒環境下由於共享資料導致的資料不一致和執行緒安全問題。

下面是一個簡單的示例,展示瞭如何使用ThreadLocal來建立執行緒區域性變數:

public class ThreadLocalExample {
// 建立一個ThreadLocal例項
private static final ThreadLocal threadLocal = new ThreadLocal() {
@Override
protected Integer initialValue() {
return 0; // 初始化值為0
}
};

public static void main(String[] args) {
// 建立兩個執行緒
Thread thread1 = new Thread(() -> {
threadLocal.set(threadLocal.get() + 1); // 設定執行緒區域性變數的值
System.out.println(Thread.currentThread().getName() + " " + threadLocal.get());
});

Thread thread2 = new Thread(() -> {
threadLocal.set(threadLocal.get() + 1); // 設定執行緒區域性變數的值
System.out.println(Thread.currentThread().getName() + " " + threadLocal.get());
});

// 啟動執行緒
thread1.start();
thread2.start();
}
}
在這個示例中,每個執行緒都有自己的threadLocal變數的副本,每個執行緒對threadLocal的修改不會影響其他輸出結果將會是:

Thread-0 1
Thread-1 1
每個執行緒都輸出了1,這表明每個執行緒操作的是自己獨立的執行緒區域性變數副本。

什麼是死鎖(Deadlock)?如何避免死鎖?
死鎖是指兩個或更多的執行緒因為競爭資源而造成的一種互相等待的現象,若無外力作用,這些執行緒都將無法繼續當兩個執行緒相互等待對方釋放資源時,就會發生死鎖。

為了避免死鎖,可以採取以下策略:

避免迴圈等待:透過為資源分配唯一的編號,並要求執行緒總是以升序(或降序)請求資源,可以打破迴圈等待的條件。

避免持有並等待:一種方法是要求執行緒一次性請求所有需要的資源,如果某個資源不可用,則執行緒等待。另一種方法是允許執行緒請求資源,但如果不能立即獲得所有資源,則釋放已經持有的資源。

設定超時時間:為執行緒獲取資源設定超時時間,如果超時則釋放已持有的資源,並稍後重試。

使用死鎖預防演算法:例如銀行家演算法,它可以在分配資源之前預測是否會發生死鎖,並據此做出決策。

使用鎖順序:確保所有執行緒都按照相同的順序請求鎖,這可以減少死鎖的可能性。

使用鎖超時和中斷:為鎖操作設定超時時間,並在適當的時候允許執行緒中斷,這樣執行緒可以在等待資源時響應中斷。

使用死鎖檢測工具:有些工具可以在執行時檢測死鎖,並在檢測到死鎖時採取行動,如中斷執行緒或重置資源狀態。

Java中的鎖機制有哪些?
Java中提供了多種鎖機制來控制對共享資源的訪問,包括內建鎖(也稱為監視器鎖或互斥鎖)、顯示鎖(如ReentrantLock)、讀寫鎖(如ReentrantReadWriteLock)以及訊號量(Semaphore)等

內建鎖(Synchronization):透過synchronized關鍵字實現,它可以修飾方法或程式碼塊。當一個執行緒進入一個物件的synchronized方法或程式碼塊時,它會獲得該物件的鎖,其他執行緒則不能同時訪問該物件的synchronized方法或程式碼塊。

顯示鎖(ReentrantLock):java.util.concurrent.locks.ReentrantLock類實現了顯示鎖。顯示鎖需要顯式地呼叫lock()方法獲取鎖,並在finally塊中呼叫unlock()方法釋放鎖。它提供了更高的靈活性和更多的功能,比如嘗試獲取鎖(tryLock())、鎖是否被某個執行緒持有(isHeldByThread())等。

讀寫鎖(ReentrantReadWriteLock):java.util.concurrent.locks.ReentrantReadWriteLock類實現了讀寫鎖讀寫鎖允許多個執行緒同時讀取共享資源,但只允許一個執行緒寫入共享資源。這可以提高併發效能,因為讀操作通常不會修改資料,所以多個讀執行緒可以同時訪問資料。

訊號量(Semaphore):java.util.concurrent.Semaphore類實現了訊號量。訊號量用於控制訪問某個資源的執行緒數量。透過呼叫acquire()方法獲取訊號量,如果訊號量可用則立即返回,否則執行緒會等待;透過呼叫release()方法釋放訊號量,使等待的執行緒能夠獲取到訊號量。

什麼是活鎖(Livelock)?它與死鎖有何不同?
活鎖是指兩個或更多的執行緒無限期地重試執行一個操作,但由於競爭條件,該操作總是失敗。與死鎖不同,活鎖中的執行緒沒有互相等待,而是都在積極地嘗試執行操作。

活鎖和死鎖的區別在於:

死鎖:執行緒之間相互等待對方釋放資源,導致沒有任何執行緒能夠繼續執行。

活鎖:執行緒都在積極嘗試執行,但由於競爭或其他原因,總是失敗,導致它們無法達到目標狀態。

Java中的volatile關鍵字有什麼作用?
volatile是Java中的一個關鍵字,它用於宣告變數。當一個變數被宣告為volatile時,意味著這個變數的值可能會被意外地、併發地改變,因此係統每次使用它時都會直接從主記憶體中讀取,而不是使用儲存在某些地方的備份。此外,寫入一個volatile變數也會通知JVM,這個值已經被改變。

volatile主要有三個作用:

保證可見性:volatile關鍵字能確保多個執行緒能正確地處理共享變數,當一個執行緒修改了一個共享變數的值,新值對其他執行緒來說是立即可見的。

禁止指令重排序:為了確保volatile變數的操作有序性,編譯器和處理器不會對其指令進行重排序。

不保證原子性:volatile並不能保證複合操作的原子性,比如自增操作

什麼是樂觀鎖和悲觀鎖?
樂觀鎖和悲觀鎖是兩種常見的併發控制策略,它們在對資料進行修改時採用了不同的機制和態度。

樂觀鎖:樂觀鎖假設多個執行緒併發執行時不會彼此衝突,直到提交更新資料時才會檢查是否有衝突。樂觀鎖通常是透過資料版本記錄機制來實現。在讀取資料時,會同時讀取資料的版本號,當更新資料時,會判斷版本號是否發生變化,如果版本號未變,則更新資料並增加如果版本號已變,則說明有其他執行緒修改了資料,此時更新操作會失敗,需要重新嘗試。

悲觀鎖:悲觀鎖則認為在資料處理過程中總是會發生衝突,因此在資料處理過程中會鎖定資料,防止其他執行緒對其進行修改。悲觀鎖的實現通常依賴於資料庫的鎖機制,比如行鎖、表鎖等。在Java中,悲觀鎖可以透過synchronized關鍵字或ReentrantLock等鎖機制來實現。

什麼是CAS操作?它在Java中是如何實現的?
CAS(Compare-and-Swap)操作是一種無鎖機制,它包含三個運算元——記憶體位置(V)、預期原值(A)和更新值(B)。執行CAS操作時,會將記憶體位置V的值與預期原值A進行比較,如果相匹配,那麼處理器會自動將該記憶體位置V的值更新為B。如果不匹配,處理器則不做任何操作。無論哪種情況,它都會在CAS指令之前返回該位置的值。這一過程是原子的,即在CAS指令執行期間,V的值不會被其他執行緒修改。

在Java中,CAS操作是透過sun.misc.Unsafe類中的方法實現的,該類提供了對Java物件記憶體的底層訪問許可權。Java併發包java.util.concurrent.atomic中的原子類(如AtomicInteger、AtomicLong等)大量使用了CAS操作來實現執行緒安全的數值更新。這些原子類提供了執行緒安全的方式來對整數值進行自增、自減、比較並交換等操作,而無需使用synchronized關鍵字。

Java中的阻塞佇列和非阻塞佇列有什麼區別?
阻塞佇列和非阻塞佇列在Java中的主要區別在於它們在佇列為空或滿時的行為。

阻塞佇列:當佇列為空時,從佇列中獲取元素的執行緒會被阻塞,直到有元素可用;當佇列已滿時,嘗試向佇列中新增元素的執行緒也會被阻塞,直到佇列中有空位。Java中的BlockingQueue介面及其實現類(如ArrayBlockingQueue、LinkedBlockingQueue等)提供了阻塞佇列的實現。

非阻塞佇列:非阻塞佇列在佇列為空時,獲取元素的執行緒不會阻塞,而是立即返回一個特殊值(如null或特定標記),或者返回一個表示佇列為空的訊號。類似地,當佇列已滿時,新增元素的執行緒也不會阻塞,而是立即返回一個特殊值或訊號。Java中的ConcurrentLinkedQueue和ConcurrentLinkedDeque等類提供了非阻塞佇列的實現。

什麼是執行緒池中的拒絕策略?Java中提供了哪些拒絕策略?
執行緒池中的拒絕策略是指當執行緒池已經關閉或達到飽和狀態(即佇列已滿且沒有可用的工作執行緒),新提交的任務無法被處理時所採取的策略。

Java中提供了以下四種拒絕策略:

AbortPolicy:這是預設的拒絕策略,當新任務無法被處理時,它會丟擲RejectedExecutionException異常。

CallerRunsPolicy:當新任務無法被處理時,它會在呼叫者的執行緒中直接執行該任務。這通常適用於執行器內部任務的小任務。

DiscardPolicy:當新任務無法被處理時,它會被直接丟棄,不會丟擲任何異常或進行任何處理。

DiscardOldestPolicy:當新任務無法被處理時,它會丟棄佇列中最舊的任務,然後嘗試重新提交新任務。

可以透過ThreadPoolExecutor的setRejectedExecutionHandler方法來設定自定義的拒絕策略。

Java中的Future和CompletableFuture有什麼區別?
Future是Java併發程式設計中用於表示非同步計算的結果的介面,它允許你在計算完成後獲取結果或處理異常。Future介面主要定義了三個方法:get()、cancel()和isDone(),分別用於獲取結果、取消任務和檢查

CompletableFuture是Java 8引入的一個功能更強大、更靈活的類,它實現了Future和CompletionStage介面,提供了函數語言程式設計的方法來處理非同步程式設計。CompletableFuture支援鏈式程式設計,允許你以非阻塞的方式處理非同步結果,並且提供了多種方法來組合和轉換非同步結果。

與Future相比,CompletableFuture的主要優勢包括:

函數語言程式設計風格:CompletableFuture提供了thenApply、thenAccept、thenRun等方法,允許你以函數語言程式設計的方式處理非同步結果。

異常處理:CompletableFuture提供了exceptionally方法,允許你指定一個函式來處理可能發生的異常。

組合多個非同步操作:CompletableFuture提供了allOf、anyOf、thenCombine、thenAcceptBoth等方法,可以組合多個非同步操作,等待它們全部完成或任何一個完成,並以某種方式處理它們的結果。

非同步轉同步:CompletableFuture提供了get()和join()方法,可以等待非同步操作完成並獲取結果,但這些方法在內部使用了阻塞方式等待結果,因此在等待期間不會浪費執行緒資源。

更強大的取消功能:CompletableFuture提供了更靈活的取消機制,允許你檢查任務是否已經被取消,以及是否可以被取消

什麼是Java中的Fork/Join框架?
Java中的Fork/Join框架是Java 7引入的一個用於並行執行任務的框架,它採用分而治之的策略,將一個大任務拆分成多個小任務並行執行,然後再將結果合併起來。Fork/Join框架使用兩個核心類:ForkJoinPool(執行緒池)和ForkJoinTask(任務)。

Fork/Join框架適用於以下場景:

任務可以拆分:任務可以被拆分成多個子任務,並且這些子任務可以並行執行。

任務量較大:當需要處理的任務量較大時,使用Fork/Join框架可以充分利用多核處理器的平行計算能力,提高程式的執行效率。

任務之間沒有依賴關係或依賴關係較少:Fork/Join框架適用於任務之間沒有依賴關係或依賴關係較少的情況。如果有強烈的依賴關係,可能不適合使用Fork/Join框架。

結果需要合併:最終的結果需要從各個子任務的結果中合併得到。

什麼是Java中的執行緒區域性變數(ThreadLocal)?它有什麼作用?
Java中的ThreadLocal類提供了執行緒區域性變數。這些變數與其他普通變數的區別在於,每個訪問該變數的執行緒都有自己獨立、初始化的變數副本。ThreadLocal例項通常作為靜態欄位出現在類中,以關聯執行緒和執行緒上下文。

作用:

執行緒隔離:ThreadLocal允許你在多執行緒環境中為每個執行緒儲存一個獨立的變數副本,從而實現執行緒之間的資料隔離。這對於需要在不同執行緒間保持獨立狀態的資訊非常有用,例如資料庫連線、事務資訊等。

避免資料共享:透過使用ThreadLocal,你可以避免在多執行緒環境中共享資料,從而減少了執行緒間資料競爭和同步的需求,

簡化程式碼:在某些情況下,使用ThreadLocal可以簡化程式碼,避免顯式地傳遞引數或上下文物件。

需要注意的是,ThreadLocal使用不當可能會導致記憶體洩漏,因為每個執行緒都會持有一個ThreadLocal變數的副本,如果執行緒不再使用,但ThreadLocal變數沒有被正確清理,那麼這些副本就不會被垃圾收集器回收。因此,在使用ThreadLocal時,需要確保在不再需要時正確地移除這些變數。

什麼是Java的記憶體模型?它有什麼重要性?
Java記憶體模型(Java Memory Model, JMM)是Java虛擬機器(JVM)規範中定義的一種抽象概念,它描述了Java程式中各種變數(執行緒共享變數)的訪問規則。JMM定義了JVM在多執行緒環境中如何處理原子性、可見性和有序性的問題。

重要性:

確保正確的併發行為:JMM透過定義一系列規則和約束,確保了在多執行緒環境中對共享變數的訪問和操作是正確的,從而避免了資料不一致、競態條件和其他併發問題。

提高程式效能:合理的記憶體模型可以允許編譯器和處理器對程式碼進行更多的最佳化,從而提高程式的執行效率。例如,透過允許編譯器對程式碼進行重排序,可以在不改變程式執行結果的前提下提高程式的效能。

簡化程式設計模型:JMM提供了一個統一的、抽象的記憶體模型,程式設計師無需關心底層硬體和作業系統的具體細節,只需要按照JMM的規則編寫程式碼即可。這大大簡化了併發程式設計的複雜性。

什麼是Java中的原子變數(Atomic Variables)?它們是如何保證執行緒安全的?
在Java中,原子變數是指被設計為在多執行緒環境中能夠被安全、原子地更新的變數。Java的java.util.concurrent.atomic包提供了一系列原子變數類,如AtomicInteger、AtomicLong、AtomicReference等。

原子變數如何保證執行緒安全:

原子操作:原子變數透過利用硬體級別的原子操作來保證更新操作的原子性。這意味著這些操作在執行過程中不會被其他執行緒打斷,從而保證了操作的完整性和一致性。

無鎖機制:與傳統的使用synchronized或Lock等顯式鎖來保證執行緒安全的機制不同,原子變數採用了一種無鎖(lock-free)的設計。這意味著它們在多執行緒環境下不會引起阻塞,從而減少了執行緒之間的競爭和等待時間,提高了併發效能。

記憶體可見性:原子變數還透過特殊的記憶體操作來保證變數的更新對其他執行緒是立即可見的。這通常涉及到Java記憶體模型中的volatile語義,確保寫入的值會立即同步到主記憶體,並且其他執行緒會立即看到更新後的值。

順序性:原子變數還保證了操作的順序性,即多個原子操作之間的執行順序符合程式設計師的預期。這有助於防止由於指令重排序導致的問題。

什麼是Java中的鎖?有哪些常見的鎖實現?
在Java中,鎖是一種用於控制多個執行緒對共享資源的訪問的機制。它確保了在任何時候只有一個執行緒可以執行某個程式碼塊或方法,從而防止了資料的不一致性和其他併發問題。

常見的鎖實現包括:

內建鎖(或稱為同步鎖):透過synchronized關鍵字實現。它可以用於方法或程式碼塊,確保同一時間只有一個執行緒可以執行被保護的程式碼。

ReentrantLock:java.util.concurrent.locks.ReentrantLock是Java併發包中提供的一個可重入與內建鎖相比,它提供了更豐富的功能,如可中斷鎖獲取、嘗試獲取鎖、公平鎖策略等。

讀寫鎖(ReadWriteLock):java.util.concurrent.locks.ReadWriteLock介面及其實現類(如ReentrantReadWriteLock)允許多個執行緒同時讀取共享資源,但只允許一個執行緒寫入。這可以提高讀密集型應用程式的併發效能。

訊號量(Semaphore):java.util.concurrent.Semaphore是一種用於控制多個執行緒訪問特定資源的計數器。它維護一個可用的許可證數量,並根據這個數量來決定是否允許執行緒繼續執行。

CountDownLatch:雖然不是一種鎖,但java.util.concurrent.CountDownLatch是一種同步工具,用於讓一個或多個執行緒等待其他執行緒完成一組操作後再繼續執行。

什麼是死鎖?
死鎖是指兩個或更多的執行緒無限期地等待一個資源,導致它們都無法繼續執行。這通常發生在每個執行緒都持有一些資源並等待獲取其他執行緒持有的資源時,形成一個迴圈等待條件。

避免死鎖的方法包括:

避免迴圈等待:確保執行緒總是以相同的順序請求資源。這樣,即使多個執行緒請求同一組資源,也不會形成迴圈等待條件。

資源分配超時:為資源請求設定超時時間。如果執行緒在超時時間內未能獲得所需資源,則釋放已持有的資源並嘗試其他策略。

資源分級:將資源分級並賦予不同的優先順序。執行緒總是先請求低階別的資源,然後再請求高階別的資源。這有助於減少死鎖的可能性。

使用鎖順序:確保所有執行緒都按照相同的順序請求鎖。這樣可以防止執行緒之間的迴圈等待。

死鎖檢測和恢復:在某些情況下,可能難以完全避免死鎖。在這種情況下,可以定期檢測死鎖並採取措施來恢復,例如透過執行緒中斷或資源搶佔來解除死鎖狀態。

設計模式面試題
什麼是設計模式?為什麼要使用設計模式?
設計模式是在軟體開發中反覆出現的問題的最佳解決方案。它是經過驗證的經驗總結,為常見問題提供了一套可重用的解決方案。

使用設計模式可以幫助開發人員更快地構建高質量的軟體,提高程式碼的可讀性、可維護性和可擴充套件性。

請解釋單例模式,並給出其一個應用場景。
單例模式確保一個類只有一個例項,並提供一個全域性訪問點。

應用場景:配置檔案的讀取、資料庫連線池、執行緒池等。

什麼是工廠模式?它有哪些型別?
工廠模式是一種建立型設計模式,它提供了一種建立物件的最佳方式,而無需指定具體要建立的類。

工廠模式有兩種主要型別:簡單工廠模式和工廠方法模式。

解釋觀察者模式,並給出一個實際應用。
觀察者模式是一種行為設計模式,它定義了一種一對多的依賴關係,讓多個觀察者物件同時監聽某一個主題物件,當主題物件狀態改變時,所有依賴它的觀察者物件都會收到通知並自動更新。

實際應用:GUI中的事件處理、訊息佇列、日誌系統等。

請描述介面卡模式,並給出一個使用場景。
介面卡模式是一種結構型設計模式,它允許一個類的介面與另一個類的介面不相容時,仍然可以使用。它使得原本由於介面不相容而不能一起工作的類可以一起工作。

使用場景:將一個類的介面轉換成客戶期望的另一個介面,使得原本不相容的介面能夠一起工作。例如,將一箇舊的類庫中的類適配到新的框架中。

解釋裝飾器模式,並給出其優勢。
裝飾器模式是一種結構型設計模式,它允許在不改變原始類結構和行為的基礎上,動態地為其新增額外的功能或責任。

優勢:可以在執行時動態地新增或刪除功能,而不需要修改原始類;提供了比繼承更加靈活的方式來擴充套件功能;遵循了開閉原則,對擴充套件開放,對修改封閉。

請列舉幾種常見的設計模式,並簡要描述它們的作用。
建造者模式:將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示。

原型模式:透過複製現有物件來建立新物件,而無需重新例項化。

橋接模式:將抽象部分與實現部分分離,使它們可以獨立變化。

組合模式:將物件組合成樹形結構以表示“部分整體”的層次結構,使得使用者對單個物件和複合物件的使用具有一致性。

什麼是策略模式?它在什麼情況下最有用?
策略模式定義了一系列的演算法,並將每一個演算法封裝起來,使它們可以互相替換。策略模式讓演算法獨立於使用它的客戶端。

在有多種演算法可能的情況下,策略模式最有用。例如,一個排序程式可以根據使用者選擇使用不同的排序演算法(如氣泡排序、快速排序、歸併排序等)。

你能解釋一下模板方法模式嗎?它在哪些場景中特別適用?
模板方法模式是一種行為設計模式,它在一個方法中定義了一個演算法的骨架,允許子類在不改變演算法結構的情況下重定義某些步驟的具體內容。

模板方法模式在以下場景中特別適用:實現一個演算法的不變部分,並允許子類在不改變演算法結構的情況下重新定義某些步驟。例如,在繪製圖形時,可以定義一個繪製圖形的模板方法,允許子類根據不同的圖形型別重寫繪製步驟。

解釋代理模式,並給出一個實際應用。
代理模式為其他物件提供了一種代理,以控制對這個物件的訪問。這通常用於在物件間新增額外的職責,如日誌記錄、安全檢查、遠端物件訪問等。

實際應用:遠端代理(RPC)、虛擬代理(延遲載入)、保護代理(許可權檢查)、智慧引用代理(自動釋放資源)

你如何理解“設計原則”和“設計模式”之間的關係?
設計原則是一組指導我們如何設計軟體的規則或指導方針,如SOLID原則(單一職責原則、開放封閉原則、里氏替換原則、介面隔離原則和依賴倒置原則)。

設計模式則是這些原則的具體實現,是解決在軟體設計過程中遇到的常見問題的最佳實踐。設計模式是設計原則的具體化和例項化。

在設計模式中,什麼是“開閉原則”?
開閉原則(OCP)是物件導向設計的基本原則之一,它要求軟體實體(類、模組、函式等)應當對擴充套件開放,對修改封閉。換言之,當應用程式需要增加新功能時,應當儘量透過新增新程式碼來實現,而不是修改現有的程式碼。

什麼是外觀模式?它在哪些場景下特別有用?
外觀模式是一種結構型設計模式,它為子系統中的一組介面提供一個統一的高層介面,使得子系統更加容易使用。

外觀模式在以下場景下特別有用:

客戶端不需要知道子系統內部的複雜性和依賴關係。

子系統之間有很強的依賴關係,客戶端只需要簡單的介面訪問。

當需要構建一個層次結構的子系統時,透過外觀模式可以簡化客戶端與子系統之間的互動。

請描述一下迭代器模式,並說明它的優點
迭代器模式是一種行為型設計模式,它提供了一種方法順序訪問一個聚合物件中的各個元素,而又不需要暴露該物件的內部表示。

迭代器模式的優點包括:

它支援以多種方式遍歷一個聚合物件。

迭代器簡化了聚合的介面。

在同一聚合上可以有多個遍歷。

在遍歷期間修改聚合結構,迭代器仍然有效。

什麼是組合模式?請給出一個使用組合模式的場景
組合模式是一種結構型設計模式,它允許你將物件組合成樹形結構來表示“部分整體”的層次結構,並且能像單個物件一樣使用它們。

使用組合模式的場景通常包括:

你想要表示物件的部分以及整體層次的結構,如檔案和目錄的結構、UI元件的樹形結構等

你希望使用者能忽略組合物件與單個物件的不同,統一地使用它們。

什麼是訪問者模式?它在什麼情況下最有用?
訪問者模式是一種行為型設計模式,它允許你在不修改已有類的結構的情況下增加新的操作。訪問者模式把操作邏輯從物件中分離出來,封裝在獨立的訪問者類中。

訪問者模式在以下情況下最有用:

物件的結構相對穩定,但經常需要在此結構上定義新的操作。

需要對一個物件的組合結構進行很多不相關的操作。

你想避免讓這些操作“汙染”這些物件的類。

在設計模式中,如何區分狀態模式和策略模式?
狀態模式關注物件的狀態變化,根據物件的不同狀態執行不同的行為。它主要用於當物件的行為依賴於其狀態時。

策略模式關注的是一組演算法,允許在執行時根據不同的策略選擇演算法的行為。它主要用於有多種演算法可供選擇,並且這些演算法可以互相替換的場景。

Java NIO面試題
什麼是Java NIO?它與傳統的Java I/O有什麼不同?
Java NIO是Java的一個新I/O框架,提供了非阻塞I/O操作,以更好地處理大量併發連線。與傳統的Java I/O相比,Java NIO更加適合處理大量併發讀寫操作,因為它減少了執行緒間的上下文切換,從而提高了應用的效能和可擴充套件性

在Java NIO中,主要有哪些元件?請簡要描述它們的作用。
Java NIO的主要元件包括:

Buffer:用於儲存資料,提供讀寫功能。

Channel:用於進行I/O操作,如檔案讀寫、網路讀寫等。

Selector:用於監視多個Channel的狀態變化,從而實現單個執行緒處理多個Channel的能力。

解釋一下ByteBuffer,它是如何工作的?
ByteBuffer是Java NIO中的一個核心元件,它用於儲存資料並提供讀寫功能。ByteBuffer可以處於不同的模式(如讀模式、寫模式),並且可以透過呼叫相應的方法來讀寫資料。此外,ByteBuffer還提供了一些標記和位置功能,用於方便地處理資料。

Channel和Buffer在Java NIO中起什麼作用?
Channel在Java NIO中用於進行I/O操作,如檔案讀寫、網路讀寫等。它提供了非阻塞I/O操作的能力,可以更加高效地處理大量併發連線。Buffer則是用於儲存資料的容器,它與Channel配合使用,以實現資料的讀寫操作。

Java NIO中的Selector是什麼?它如何工作?
Selector在Java NIO中用於監視多個Channel的狀態變化。透過Selector,我們可以使用一個單獨的執行緒來管理多個Channel,從而實現高效的併發處理。當某個Channel的狀態發生變化時(如可讀、可寫等),Selector會通知相應的處理執行緒,以便及時處理該Channel的I/O操作。

阻塞I/O和非阻塞I/O有什麼區別?Java NIO是如何處理這兩種I/O的?
阻塞I/O是指在進行I/O操作時,如果資料未準備好,則執行緒會被阻塞,直到資料準備好為止而非阻塞I/O則不會阻塞執行緒,而是立即返回一個結果,如果資料未準備好,則結果可能為空或表示資料未準備好。Java NIO提供了對非阻塞I/O的支援,可以透過設定Channel為非阻塞模式來實現。

Java NIO中的Scatter/Gather是什麼?請給出使用示例。
Scatter/Gather是Java NIO中的一種資料讀寫方式。Scatter表示將資料從Channel讀入到多個Buffer中,而Gather表示將多個Buffer中的資料寫入到Channel中。這種方式可以減少資料的複製次數,提高資料處理的效率。

Java NIO中的FileChannel有哪些主要特性?
FileChannel是Java NIO中用於檔案操作的Channel。它提供了一些傳統檔案I/O不具備的特性,如對映檔案到記憶體、支援檔案鎖等。此外,FileChannel還支援非同步I/O操作,可以與其他非同步API(如CompletableFuture)配合使用,以實現更加高效的檔案處理。

與傳統的Java I/O相比,Java NIO有哪些優點和缺點?
與傳統的Java I/O相比,Java NIO的主要優點包括:

支援非阻塞I/O操作,可以更加高效地處理大量併發連線。

提供了更加靈活的資料處理方式,如Scatter/Gather等。

支援非同步I/O操作,可以實現更加高效的檔案和網路處理。

然而,Java NIO也有一些缺點:

程式設計模型相對複雜,需要更多的程式碼和邏輯來處理I/O操作。

對於某些簡單的I/O操作,Java NIO可能不如傳統的Java I/O那麼高效。

請描述一下Java NIO.2(從Java 7開始)中引入的新特性。
Java NIO.2引入了以下新特性:

檔案系統的改進:提供了新的檔案系統API,可以更方便地訪問和操作檔案系統中的檔案和目錄。

非同步檔案I/O:提供了非同步檔案讀寫的支援,可以與其他非同步API(如CompletableFuture)配合使用,以實現更加高效的檔案處理。

Path和Paths類:提供了更加靈活和方便的路徑處理方式。

WatchService:提供了一個用於監視檔案系統變化的API,可以方便地實現檔案系統的監聽和通知功能。

在Java NIO中,如何正確關閉Channel和Buffer?
在Java NIO中,正確關閉Channel和Buffer是非常重要的,以避免資源洩露。關閉Channel通常是透過呼叫其close()方法實現的,而關閉Buffer則不需要顯式呼叫任何方法,因為Buffer不是資源管理物件。當Buffer不再被使用時,Java的垃圾回收器會自動回收其佔用的記憶體。然而,為了安全起見,最好在finally塊中關閉Channel,以確保即使發生異常,Channel也能被正確關閉。

Java NIO中,Selector為什麼比傳統的多執行緒I/O模型更高效?
與傳統的多執行緒I/O模型相比,Selector在Java NIO中更加高效,因為它可以監視多個Channel的狀態變化,並在單個執行緒中處理這些Channel的I/O操作。這意味著我們不需要為每個Channel分配一個獨立的執行緒,從而減少了執行緒間的上下文切換和競爭,提高了系統的效能和可擴充套件性。

解釋一下Java NIO中的零複製(Zero-Copy)技術,它在哪些場景下特別有用?
零複製(Zero-Copy)技術是一種減少資料在記憶體和磁碟之間不必要複製的技術。在Java NIO中,零複製技術可以透過使用MappedByteBuffer或者FileChannel的transferTo/transferFrom方法來實現。這些方法可以直接將資料從檔案或網路Channel傳輸到應用程式的Buffer中,或者從Buffer傳輸到檔案或網路Channel中,而無需先將資料複製到中間緩衝區。這種技術在處理大量資料時特別有用,因為它可以減少I/O操作的次數,提高資料傳輸的效率。

Java NIO中的FileLock是什麼?它在檔案併發訪問中起什麼作用?
FileLock是Java NIO中用於檔案併發訪問控制的一個機制。它允許一個應用程式請求對檔案的獨佔訪問權,從而防止其他應用程式同時對該檔案進行寫操作。透過使用FileLock,我們可以實現檔案的同步訪問,確保多個應用程式之間的資料一致性。

Java NIO中的Pipeline和ChannelHandler是如何工作的?它們在什麼場景下會被使用?
在Java NIO中,Pipeline和ChannelHandler通常用於構建複雜的網路應用,如協議編解碼、資料轉換等。Pipeline可以看作是一個處理鏈,它包含了一系列的ChannelHandler,每個ChannelHandler負責處理特定型別的I/O事件。當Channel上的事件發生時,Pipeline會按照ChannelHandler的新增順序依次呼叫它們來處理該事件。這種機制使得我們可以更加靈活地處理網路I/O,並且可以透過新增或刪除ChannelHandler來擴充套件或修改應用的功能。

什麼是分散(Scatter)和聚集(Gather)操作?在Java NIO中如何使用它們?
分散(Scatter)和聚集(Gather)是Java NIO中的兩種資料傳輸方式。分散操作允許你將資料從一個Channel分散地寫入到多個Buffer中,而聚集操作則允許你將多個Buffer中的資料聚集起來寫入到一個Channel中在Java NIO中,你可以使用Channel.read(ByteBuffer[] buffers)方法進行分散操作,使用Channel.write(ByteBuffer[] buffers)方法進行聚集操作。這些方法都接受一個ByteBuffer陣列作為引數,允許你一次讀取或寫入多個Buffer中的資料。

Java NIO中的Selector是如何選擇已就緒的Channel的?
在Java NIO中,Selector使用內部機制來監視已就緒的Channel。具體來說,Selector會註冊一組Channel,並在後臺執行緒中迴圈檢查這些Channel的狀態。當某個Channel的狀態發生變化(例如可讀、可寫等)時,Selector會將其加入到已就緒的Channel集合中,並通知相應的處理執行緒進行處理。這樣,Selector就能夠實現單個執行緒管理多個Channel的能力,提高了系統的併發處理能力。

什麼是ByteOrder?它在Java NIO的ByteBuffer中有什麼作用?
ByteOrder是Java NIO中的一個重要概念,它決定了ByteBuffer中資料的位元組順序。Java NIO提供了兩種位元組順序:BIG_ENDIAN和LITTLE_ENDIAN。BIG_ENDIAN表示大端序,即高位位元組在前;LITTLE_ENDIAN表示小端序,即低位位元組在前。在ByteBuffer中,你可以透過呼叫order(ByteOrder order)方法來設定位元組順序。這個設定會影響到ByteBuffer的讀寫操作,確保資料按照正確的順序進行傳輸和處理。

如何在Java NIO中實現非阻塞的讀寫操作?
在Java NIO中,要實現非阻塞的讀寫操作,你需要將Channel設定為非阻塞模式。這可以透過呼叫Channel的configureBlocking(false)方法來實現。在非阻塞模式下,讀寫操作不會阻塞當前執行緒,而是立即返回。如果資料未準備好,讀寫操作會返回一個特殊值(如0或-1),表示操作未完成。你可以透過迴圈呼叫讀寫方法來等待資料準備好,或者使用Selector來監視Channel的狀態變化,從而實現非阻塞的I/O操作。

Java NIO中的NIO.1和NIO.2有什麼區別?
Java NIO.1和NIO.2之間的主要區別在於功能和特性上的擴充套件。NIO.1是Java 1.4中引入的原始NIO API,提供了基本的非阻塞I/O操作和Channel、Buffer等而NIO.2則是在Java 7中引入的,它擴充套件了NIO的功能,引入了新的API和特性,如非同步檔案I/O、檔案系統訪問、WatchService等。NIO.2更加註重於檔案處理和併發訪問控制,提供了更加靈活和高效的檔案操作方式。此外,NIO.2還提供了對網路程式設計的改進和擴充套件,如支援更多的傳輸協議和套接字選項等。

在Java NIO中,如何使用非同步檔案I/O?
在Java NIO中,非同步檔案I/O是透過AsynchronousFileChannel類來實現的。這個類提供了一系列非同步讀寫檔案的方法,如read(ByteBuffer dst, long position, CompletionHandler<Integer, ? super AsynchronousFileChannel> handler)和write(ByteBuffer src, long position, CompletionHandler<Integer, ? super AsynchronousFileChannel> handler)。這些方法都是非阻塞的,它們立即返回並不等待I/O操作完成。當I/O操作完成時,會呼叫提供的CompletionHandler的completed或failed方法。要使用非同步檔案I/O,你需要建立一個AsynchronousFileChannel例項,然後呼叫它的非同步讀寫方法,並傳遞一個CompletionHandler來處理操作結果。

Java NIO中的AsynchronousFileChannel和FileChannel有什麼區別?
AsynchronousFileChannel和FileChannel都是Java NIO中用於檔案I/O的通道,但它們之間有一些關鍵區別。FileChannel提供同步的檔案I/O操作,即讀寫操作會阻塞呼叫執行緒直到完成。而AsynchronousFileChannel則提供非同步的檔案I/O操作,讀寫操作不會阻塞呼叫執行緒,而是立即返回並在操作完成時通知應用程式此外,AsynchronousFileChannel還提供了對檔案區域的操作支援,可以同時對檔案的多個區域進行讀寫操作,提高了併發效能。

什麼是ByteBuffer的容量、限制和位置?它們之間有什麼關係?
ByteBuffer的三個關鍵屬性是容量(Capacity)、限制(Limit)和位置(Position)。容量是ByteBuffer能夠容納的資料的最大量,一旦建立就不能改變。限制是第一個不應該被讀或寫的元素索引,它定義了緩衝區的邊界。位置是下一個要被讀或寫的元素的索引,位置會自動由相應的get()和put()函式更新。在進行讀寫操作時,我們通常遵循“清零(Clear)-翻轉(Flip)-寫滿(Fill)-翻轉(Flip)-清空(Clear)”的模式來操作ByteBuffer。

如何在Java NIO中處理半關閉(Half-Closure)狀態?
在Java NIO中,半關閉(Half-Closure)狀態是指在一個通訊通道中,只有一個方向的資料傳輸被關閉,而另一個方向的資料傳輸仍然可以繼續。這在處理輸入流和輸出流時需要特別注意,以防止出現資料丟失或死鎖的情況。Java NIO提供了SocketChannel和ServerSocketChannel的shutdownInput()和shutdownOutput()方法來分別關閉輸入流和輸出流,從而實現半關閉狀態。在處理半關閉狀態時,應確保正確地處理讀寫操作,並考慮執行緒同步和異常處理的問題。

什麼是NIO的零複製(Zero-Copy)機制,它有哪些優點和限制?
Java NIO中的零複製(Zero-Copy)機制是一種減少資料複製次數以提高效能的技術。在傳統的I/O操作中,資料通常需要多次複製,例如在使用者態和核心態之間、在不同的緩衝區之間等。而零複製機制透過直接操作核心緩衝區或使用特殊的系統呼叫,避免了這些不必要的複製操作。在Java NIO中,零複製可以透過使用MappedByteBuffer(透過檔案通道對映檔案到記憶體)或使用FileChannel的transferTo()和transferFrom()方法(直接在核心中進行資料傳輸)來實現。零複製的優點是減少了資料複製次數,降低了CPU和記憶體的開銷,提高了資料傳輸的效能。然而,它也有一些限制,比如對平臺的依賴性(某些系統或檔案系統可能不支援零複製)、對資料傳輸大小和方式的限制等。

JDK新特性面試題
Java 8中引入的Lambda表示式和函式式介面是什麼?它們的主要用途是什麼?
Lambda表示式和函式式介面:Lambda表示式是Java 8中引入的一種新特性,允許我們以更簡潔、函式式的方式編寫程式碼。函式式介面則是一個只有一個抽象方法的介面,可以使用Lambda表示式來實現。Lambda表示式和函式式介面主要用於簡化程式碼,提高可讀性,並使得程式碼更易於並行處理。

請解釋Java 8中的Stream API,以及它如何簡化集合的處理?
Stream API:Stream API是Java 8中引入的一個新特性,它允許我們以宣告性方式處理集合(如List、Set等)。透過使用Stream,我們可以更方便地對集合進行過濾、對映、排序、聚合等操作,而無需編寫繁瑣的迴圈程式碼。

Java 9中引入了哪些重要的新特性?
Java 9新特性:Java 9引入了許多新特性,包括模組系統(用於改善程式碼組織和依賴管理)、改進的Java平臺模組系統(JPMS)、JShell(一個互動式工具,用於評估Java程式碼片段)、集合工廠方法(簡化集合的建立)、私有介面方法(允許在介面中定義私有方法)等。

請描述Java 11中的區域性變數型別推斷(var關鍵字)是如何工作的?
區域性變數型別推斷(var關鍵字):在Java 11中,可以使用var關鍵字來推斷區域性變數的型別。編譯器會根據變數的初始化表示式來推斷其型別,使得程式碼更加簡潔。需要注意的是,var關鍵字只能用於區域性變數,不能用於類、方法或欄位的宣告。

Java 12中引入了哪些值得注意的新特性?
Java 12新特性:Java 12引入了一些新特性,包括Switch表示式(使得switch語句更加靈活和強大)、區域性變數語法改進(允許在lambda表示式和方法引用中使用var關鍵字)、以及新的垃圾收集器(如Shenandoah GC)等。

請解釋Java 14中引入的例項方法模式匹配(Pattern Matching for Instance Methods)是什麼,以及它的用途?
例項方法模式匹配:Java 14中引入的例項方法模式匹配允許我們在方法中使用更靈活的模式匹配語法來匹配引數的型別和值。這可以使得程式碼更加簡潔、易讀和易於維護。

Java 15中的密封類(Sealed Classes)是什麼?它解決了什麼問題?
密封類(Sealed Classes):Java 15中的密封類是一種限制類繼承的新特性。它允許我們定義一個類集合作為某個類的子類候選者,從而限制其他類繼承該類。這有助於提高程式碼的安全性和可維護性。

Java 16中引入的JEP 396(Record Classes)是什麼?它如何簡化資料載體的編寫?
Record Classes:Java 16中引入的Record Classes是一種簡化資料載體編寫的特性。透過使用record關鍵字定義類,我們可以自動生成類的建構函式、getter方法、equals()方法、hashCode()方法等,從而減少程式碼量並提高程式碼的可讀性。

Java 17中的JEP 409(密封上下文(Sealed Contexts))是什麼?它在哪些場景中特別有用?
密封上下文(Sealed Contexts):Java 17中的密封上下文是一種擴充套件密封類功能的新特性。它允許我們定義一組相關的密封類,並在這些類之間共享上下文資訊。這有助於在複雜的系統中更好地組織和管理程式碼。

請談談Java 18中的JEP 420(switch表示式(Switch Expressions))如何改進switch語句的語法和功能
switch表示式:Java 18中的switch表示式是一種改進後的switch語句語法。它允許我們使用更簡潔、易讀的方式編寫switch語句,並支援多種型別的匹配(如字串、列舉等)以及表示式結果作為case這有助於提高程式碼的可讀性和可維護性。

請描述Java 13中的哪些特性對提升程式效能有幫助?
ZGC(Z-Garbage Collector):一個可伸縮的低延遲垃圾收集器,旨在支援堆大小超過TB的應用。

動態類資料共享(CDS):改進了Java類資料的共享機制,減少了JVM啟動時間和記憶體佔用。

改進了G1垃圾收集器:透過減少停頓時間和增加吞吐量來提高效能。

Java 14中引入的null值的改進是什麼?它如何幫助減少空指標異常?
引入了新的@NotNull和@Nullable註解,用於明確指示變數或引數是否可以為null。這有助於增強程式碼的可讀性,並允許編譯器進行更好的靜態分析,從而有助於減少空指標異常。

Java 15中的文字塊(Text Blocks)是如何簡化字串處理的?
Java 15引入了文字塊,它允許我們以更簡潔、易讀的方式編寫多行字串。透過保留換行符和縮排,文字塊使得處理如SQL查詢、HTML或JSON等格式化文字更加容易。

請解釋Java 16中引入的switch表示式的優勢是什麼?
switch表示式提供了更加緊湊、易讀的語法,支援多種型別的匹配(包括null),並且每個case都是表示式,可以直接返回值。這避免了傳統switch語句中常見的break語句和多個return語句,使得程式碼更加簡潔和直觀。

Java 17中對上下文類(Context Classes)的支援是如何簡化依賴注入的?
Java 17中對上下文類的支援透過引入新的API來簡化依賴注入這使得開發者可以更容易地管理程式的依賴關係,降低元件之間的耦合度,並提高程式碼的可維護性。

請談談Java 18中對於集合API的哪些改進是值得關注的?
Java 18繼續對集合API進行迭代和改進,可能包括新的集合型別、最佳化現有集合類的效能、改進併發集合的設計等。具體改進取決於JEP的提案和實現。

Java 19中計劃引入的新的JEP有哪些是值得關注的?
Java 19中計劃引入的新JEP可能包括改進現有特性、新增新的API、最佳化效能或增強安全性等方面的內容。具體的JEP提案和細節可以在Oracle的官方JDK釋出頁面上找到。

相關文章