網(wǎng)站制作難點seo是做什么工作內(nèi)容
前言
前文介紹了JVM相關(guān)知識,本文將重點介紹多線程相關(guān)知識以及工作中的一些經(jīng)驗。
多線程面試合集
- 什么是多線程?為什么我們需要多線程?
多線程是指在一個進程中同時執(zhí)行多個線程,每個線程可以執(zhí)行不同的任務(wù)。多線程可以提高程序的執(zhí)行效率,充分利用多核CPU的計算能力,同時處理多個任務(wù)。
- 在Java中創(chuàng)建線程有幾種方式?
繼承Thread類并重寫run()方法;
實現(xiàn)Runnable接口并重寫run()方法;
實現(xiàn)Callable接口并重寫call()方法,這種方式通常與線程池一起使用。
- 解釋一下線程的生命周期?
線程的生命周期包括新建、就緒、運行、阻塞和死亡五個狀態(tài)。新建狀態(tài)表示線程已被創(chuàng)建但尚未啟動;就緒狀態(tài)表示線程已準備好運行,等待CPU調(diào)度;運行狀態(tài)表示線程正在執(zhí)行;阻塞狀態(tài)表示線程由于某種原因暫時停止執(zhí)行;死亡狀態(tài)表示線程執(zhí)行完畢或出現(xiàn)異常而結(jié)束。
- 什么是線程安全?Java中如何實現(xiàn)線程安全?
線程安全是指在多線程環(huán)境下,程序的執(zhí)行結(jié)果不會因為線程的調(diào)度順序而改變。Java中實現(xiàn)線程安全的方式有多種,如使用synchronized關(guān)鍵字、使用Lock接口及其實現(xiàn)類、使用volatile關(guān)鍵字、使用原子類、使用線程安全的集合類等。
- 什么是死鎖?如何避免死鎖?
死鎖是指兩個或多個線程互相等待對方釋放資源而無限期地等待下去。避免死鎖的方法包括:
避免一個線程同時獲取多個鎖;
按照固定的順序獲取鎖;
使用定時鎖,即嘗試獲取鎖,若超過一定時間則放棄;
使用死鎖檢測工具來檢測和恢復死鎖。
- 解釋一下synchronized關(guān)鍵字和Lock接口的區(qū)別?
synchronized關(guān)鍵字和Lock接口都可以用來實現(xiàn)線程同步,但它們在使用方式和功能上有所不同。synchronized是Java語言的關(guān)鍵字,它可以修飾方法或代碼塊,實現(xiàn)線程同步;而Lock接口是Java.util.concurrent包中的一個接口,它提供了更靈活的線程同步機制,如支持可重入鎖、可中斷獲取鎖、嘗試獲取鎖等。
- 解釋一下AQS?
AQS,即AbstractQueuedSynchronizer,是Java中的一個抽象隊列同步器類。它是Java并發(fā)編程中的一個重要組成部分,廣泛用于實現(xiàn)ReentrantLock、Semaphore、CountDownLatch等同步工具類。
AQS的核心思想是利用一個先進先出(FIFO)的雙向隊列來管理線程的競爭和等待。AQS提供了兩種模式:獨占模式和共享模式。獨占模式是指只有一個線程可以持有同步狀態(tài),如ReentrantLock;共享模式是指多個線程可以同時持有同步狀態(tài),如Semaphore。
AQS的具體實現(xiàn)方式是通過維護一個volatile變量state表示同步狀態(tài)。當state為0時表示沒有線程占用同步狀態(tài),當state為1時表示有一個線程占用同步狀態(tài)。當多個線程競爭同步狀態(tài)時,只有一個線程可以成功占用同步狀態(tài),其余線程將加入到AQS的同步隊列中等待。當占用同步狀態(tài)的線程釋放同步狀態(tài)時,AQS會從同步隊列中選擇一個線程喚醒,使其重新嘗試獲取同步狀態(tài)。
AQS提供了一些核心方法,如acquire(int arg)嘗試獲取同步狀態(tài),如果獲取失敗則加入同步隊列并阻塞等待喚醒,直到獲取同步狀態(tài)成功。tryAcquire(int arg)嘗試獲取同步狀態(tài),如果獲取成功則返回true,否則返回false。release(int arg)釋放同步狀態(tài),通知其他線程可以嘗試獲取同步狀態(tài)。
總的來說,AQS提供了一種高效且靈活的實現(xiàn)同步器的方式,可以滿足不同的并發(fā)編程需求。
- 什么是線程池?為什么要使用線程池?
線程池是一種管理和復用線程的技術(shù),它可以避免頻繁地創(chuàng)建和銷毀線程,提高程序的性能。使用線程池的好處包括:
降低資源消耗:通過復用已創(chuàng)建的線程,減少在創(chuàng)建和銷毀線程上花費的時間以及系統(tǒng)資源的開銷;
提高響應速度:當任務(wù)到達時,任務(wù)可以不需要等到線程創(chuàng)建就能立即執(zhí)行;
提高線程的可管理性:線程池可以進行統(tǒng)一的分配、調(diào)優(yōu)和監(jiān)控。
- 介紹一下Java中的幾種線程池及其應用場景?
Java中的線程池主要包括FixedThreadPool、CachedThreadPool、SingleThreadExecutor和ScheduledThreadPool等。FixedThreadPool適用于處理CPU密集型任務(wù),控制線程最大并發(fā)數(shù);CachedThreadPool適用于處理大量短時間任務(wù),線程數(shù)會根據(jù)任務(wù)量動態(tài)調(diào)整;SingleThreadExecutor適用于需要保證順序地執(zhí)行各個任務(wù),并且在任意時間點,不會有多個線程是活動的場景;ScheduledThreadPool適用于需要定時執(zhí)行或周期性執(zhí)行任務(wù)的場景。
在真實的應用環(huán)境下我們一般都自定義ThreadPoolExecutor,并且沒有通用的配置,要根據(jù)任務(wù)執(zhí)行時間、CPU處理能力等因素綜合考慮,具體的需要結(jié)合壓測進行評估。
- ThreadLocal家族
ThreadLocal 是 Java 中的一個類,它提供了線程本地變量。這些變量與普通變量不同,因為每個訪問變量的線程都有自己的獨立初始化的變量副本。ThreadLocal 實例通常是類中的私有靜態(tài)字段,它們?yōu)槭褂迷撟兞康拿總€線程都持有一個獨立的值。
ThreadLocal 的主要作用是提供線程內(nèi)的數(shù)據(jù)隔離,避免多線程環(huán)境下的數(shù)據(jù)競爭和不一致問題。每個線程都可以獨立地改變自己的副本,而不會影響其他線程所持有的副本。
以下是 ThreadLocal 的一些關(guān)鍵點:
線程隔離:最重要的特性,確保變量對每個線程都是獨立的。
避免同步:由于數(shù)據(jù)是隔離的,因此不需要使用 synchronized 關(guān)鍵字來同步訪問。
初始值:可以通過覆蓋 initialValue() 方法來為 ThreadLocal 變量設(shè)置初始值。
內(nèi)存泄漏風險:如果不正確地使用 ThreadLocal(例如,長時間持有對象引用而不清理),可能會導致內(nèi)存泄漏。特別是在使用線程池時,因為線程池中的線程通常不會被銷毀,所以它們的 ThreadLocal 變量也不會被垃圾收集。
不適用于所有場景:ThreadLocal 適用于需要在單個線程內(nèi)維護狀態(tài)的情況,但不適用于需要在多個線程之間共享數(shù)據(jù)的場景。
如上文,ThreadLocal僅用于單線程場景
在父子線程場景下,我們要使用InheritableThreadLocal,原理是在new Thread的過程中將數(shù)據(jù)下傳
在多線程場景下,我們要使用TransmittableThreadLocal,在主線程向子線程傳遞數(shù)據(jù)。具體的原理主要通過傳輸器在主線程捕捉數(shù)據(jù)并在子線程回放快照。
- 在使用線程池時有哪些經(jīng)驗?以及遇到過哪些問題?
使用經(jīng)驗:**在配置線程池參數(shù)要綜合考慮CPU核數(shù)、單任務(wù)耗時等因素。**不可籠統(tǒng)的按照經(jīng)驗配置,這樣可能在一般場景下不會出現(xiàn)什么問題,但是在大流量場景下會爆發(fā)出一系列問題:如jvm無法承載達到的流量導致阻塞、任務(wù)積壓導致服務(wù)假死重啟等。在配置好參數(shù)之后,最好通過壓測進行目前性能的測試,并配置服務(wù)接口的限流,以防實際情況壓垮服務(wù)。在業(yè)務(wù)場景中,要進行線程隔離,以防調(diào)用不均衡導致資源分配侵襲影響核心流程。
常見問題:1. 配置線程數(shù)、隊列數(shù)太少,在回傳調(diào)用線程拒絕策略下,如有突發(fā)流量會導致主線程阻塞,長時間持續(xù)會壓垮服務(wù)。2. 線程數(shù)配置太多,導致CPU負載高,處理能力下降 3. 隊列配置太大導致任務(wù)積壓在隊列中,無法消費,從而影響某些核心節(jié)點甚至導致內(nèi)存溢出。4. 核心任務(wù)可配置拒絕策略為Call并限流,不能任其大流量進入、突發(fā)場景下最好配置拒絕策略為丟棄,以防導致調(diào)用線程阻塞 5. 最好重寫拒絕策略,并配置監(jiān)控告警、同時命名業(yè)務(wù)線程池。